diff --git a/Cargo.lock b/Cargo.lock index b6155e1..775ef39 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,13 +2,408 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "anyhow" +version = "1.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" + +[[package]] +name = "async-executor" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497c00e0fd83a72a79a39fcbd8e3e2f055d6f6c7e025f3b3d91f4f8e76527fb8" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "pin-project-lite", + "slab", +] + +[[package]] +name = "async-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" +dependencies = [ + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "windows-sys", +] + +[[package]] +name = "async-lock" +version = "3.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "event-listener" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-lite" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "libc" +version = "0.2.177" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" + +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + +[[package]] +name = "macro_rules_attribute" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65049d7923698040cd0b1ddcced9b0eb14dd22c5f86ae59c3740eab64a676520" +dependencies = [ + "macro_rules_attribute-proc_macro", + "paste", +] + +[[package]] +name = "macro_rules_attribute-proc_macro" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670fdfda89751bc4a84ac13eaa63e205cf0fd22b4c9a5fbfa085b63c1f1d3a30" + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "polling" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix", + "windows-sys", +] + +[[package]] +name = "proc-macro2" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustix" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + +[[package]] +name = "smol-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfcaedb62e0475a6898988138995ec7b1e5d116167a72bb12c7b59d0649fbbc2" +dependencies = [ + "async-executor", + "async-io", + "async-lock", + "event-listener", + "futures-lite", +] + +[[package]] +name = "syn" +version = "2.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "tytix" version = "0.1.0" dependencies = [ + "futures", + "macro_rules_attribute", + "smol-macros", "tytix-core", ] [[package]] name = "tytix-core" version = "0.1.0" +dependencies = [ + "anyhow", + "macro_rules_attribute", + "smol-macros", +] + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] diff --git a/Cargo.toml b/Cargo.toml index a1e058a..f35c1e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,4 +8,5 @@ resolver = "3" edition = "2024" version = "0.1.0" license = "EUPL-1.2" +repository = "https://git.hemera.systems/Hemera/tytix" authors = ["Marcel Müller "] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4153cd3 --- /dev/null +++ b/LICENSE @@ -0,0 +1,287 @@ + EUROPEAN UNION PUBLIC LICENCE v. 1.2 + EUPL © the European Union 2007, 2016 + +This European Union Public Licence (the ‘EUPL’) applies to the Work (as defined +below) which is provided under the terms of this Licence. Any use of the Work, +other than as authorised under this Licence is prohibited (to the extent such +use is covered by a right of the copyright holder of the Work). + +The Work is provided under the terms of this Licence when the Licensor (as +defined below) has placed the following notice immediately following the +copyright notice for the Work: + + Licensed under the EUPL + +or has expressed by any other means his willingness to license under the EUPL. + +1. Definitions + +In this Licence, the following terms have the following meaning: + +- ‘The Licence’: this Licence. + +- ‘The Original Work’: the work or software distributed or communicated by the + Licensor under this Licence, available as Source Code and also as Executable + Code as the case may be. + +- ‘Derivative Works’: the works or software that could be created by the + Licensee, based upon the Original Work or modifications thereof. This Licence + does not define the extent of modification or dependence on the Original Work + required in order to classify a work as a Derivative Work; this extent is + determined by copyright law applicable in the country mentioned in Article 15. + +- ‘The Work’: the Original Work or its Derivative Works. + +- ‘The Source Code’: the human-readable form of the Work which is the most + convenient for people to study and modify. + +- ‘The Executable Code’: any code which has generally been compiled and which is + meant to be interpreted by a computer as a program. + +- ‘The Licensor’: the natural or legal person that distributes or communicates + the Work under the Licence. + +- ‘Contributor(s)’: any natural or legal person who modifies the Work under the + Licence, or otherwise contributes to the creation of a Derivative Work. + +- ‘The Licensee’ or ‘You’: any natural or legal person who makes any usage of + the Work under the terms of the Licence. + +- ‘Distribution’ or ‘Communication’: any act of selling, giving, lending, + renting, distributing, communicating, transmitting, or otherwise making + available, online or offline, copies of the Work or providing access to its + essential functionalities at the disposal of any other natural or legal + person. + +2. Scope of the rights granted by the Licence + +The Licensor hereby grants You a worldwide, royalty-free, non-exclusive, +sublicensable licence to do the following, for the duration of copyright vested +in the Original Work: + +- use the Work in any circumstance and for all usage, +- reproduce the Work, +- modify the Work, and make Derivative Works based upon the Work, +- communicate to the public, including the right to make available or display + the Work or copies thereof to the public and perform publicly, as the case may + be, the Work, +- distribute the Work or copies thereof, +- lend and rent the Work or copies thereof, +- sublicense rights in the Work or copies thereof. + +Those rights can be exercised on any media, supports and formats, whether now +known or later invented, as far as the applicable law permits so. + +In the countries where moral rights apply, the Licensor waives his right to +exercise his moral right to the extent allowed by law in order to make effective +the licence of the economic rights here above listed. + +The Licensor grants to the Licensee royalty-free, non-exclusive usage rights to +any patents held by the Licensor, to the extent necessary to make use of the +rights granted on the Work under this Licence. + +3. Communication of the Source Code + +The Licensor may provide the Work either in its Source Code form, or as +Executable Code. If the Work is provided as Executable Code, the Licensor +provides in addition a machine-readable copy of the Source Code of the Work +along with each copy of the Work that the Licensor distributes or indicates, in +a notice following the copyright notice attached to the Work, a repository where +the Source Code is easily and freely accessible for as long as the Licensor +continues to distribute or communicate the Work. + +4. Limitations on copyright + +Nothing in this Licence is intended to deprive the Licensee of the benefits from +any exception or limitation to the exclusive rights of the rights owners in the +Work, of the exhaustion of those rights or of other applicable limitations +thereto. + +5. Obligations of the Licensee + +The grant of the rights mentioned above is subject to some restrictions and +obligations imposed on the Licensee. Those obligations are the following: + +Attribution right: The Licensee shall keep intact all copyright, patent or +trademarks notices and all notices that refer to the Licence and to the +disclaimer of warranties. The Licensee must include a copy of such notices and a +copy of the Licence with every copy of the Work he/she distributes or +communicates. The Licensee must cause any Derivative Work to carry prominent +notices stating that the Work has been modified and the date of modification. + +Copyleft clause: If the Licensee distributes or communicates copies of the +Original Works or Derivative Works, this Distribution or Communication will be +done under the terms of this Licence or of a later version of this Licence +unless the Original Work is expressly distributed only under this version of the +Licence — for example by communicating ‘EUPL v. 1.2 only’. The Licensee +(becoming Licensor) cannot offer or impose any additional terms or conditions on +the Work or Derivative Work that alter or restrict the terms of the Licence. + +Compatibility clause: If the Licensee Distributes or Communicates Derivative +Works or copies thereof based upon both the Work and another work licensed under +a Compatible Licence, this Distribution or Communication can be done under the +terms of this Compatible Licence. For the sake of this clause, ‘Compatible +Licence’ refers to the licences listed in the appendix attached to this Licence. +Should the Licensee's obligations under the Compatible Licence conflict with +his/her obligations under this Licence, the obligations of the Compatible +Licence shall prevail. + +Provision of Source Code: When distributing or communicating copies of the Work, +the Licensee will provide a machine-readable copy of the Source Code or indicate +a repository where this Source will be easily and freely available for as long +as the Licensee continues to distribute or communicate the Work. + +Legal Protection: This Licence does not grant permission to use the trade names, +trademarks, service marks, or 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 copyright notice. + +6. Chain of Authorship + +The original Licensor warrants that the copyright in the Original Work granted +hereunder is owned by him/her or licensed to him/her and that he/she has the +power and authority to grant the Licence. + +Each Contributor warrants that the copyright in the modifications he/she brings +to the Work are owned by him/her or licensed to him/her and that he/she has the +power and authority to grant the Licence. + +Each time You accept the Licence, the original Licensor and subsequent +Contributors grant You a licence to their contributions to the Work, under the +terms of this Licence. + +7. Disclaimer of Warranty + +The Work is a work in progress, which is continuously improved by numerous +Contributors. It is not a finished work and may therefore contain defects or +‘bugs’ inherent to this type of development. + +For the above reason, the Work is provided under the Licence on an ‘as is’ basis +and without warranties of any kind concerning the Work, including without +limitation merchantability, fitness for a particular purpose, absence of defects +or errors, accuracy, non-infringement of intellectual property rights other than +copyright as stated in Article 6 of this Licence. + +This disclaimer of warranty is an essential part of the Licence and a condition +for the grant of any rights to the Work. + +8. Disclaimer of Liability + +Except in the cases of wilful misconduct or damages directly caused to natural +persons, the Licensor will in no event be liable for any direct or indirect, +material or moral, damages of any kind, arising out of the Licence or of the use +of the Work, including without limitation, damages for loss of goodwill, work +stoppage, computer failure or malfunction, loss of data or any commercial +damage, even if the Licensor has been advised of the possibility of such damage. +However, the Licensor will be liable under statutory product liability laws as +far such laws apply to the Work. + +9. Additional agreements + +While distributing the Work, You may choose to conclude an additional agreement, +defining obligations or services consistent with this Licence. However, if +accepting obligations, You may act only on your own behalf and on your sole +responsibility, not on behalf of the original Licensor or 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 +the fact You have accepted any warranty or additional liability. + +10. Acceptance of the Licence + +The provisions of this Licence can be accepted by clicking on an icon ‘I agree’ +placed under the bottom of a window displaying the text of this Licence or by +affirming consent in any other similar way, in accordance with the rules of +applicable law. Clicking on that icon indicates your clear and irrevocable +acceptance of this Licence and all of its terms and conditions. + +Similarly, you irrevocably accept this Licence and all of its terms and +conditions by exercising any rights granted to You by Article 2 of this Licence, +such as the use of the Work, the creation by You of a Derivative Work or the +Distribution or Communication by You of the Work or copies thereof. + +11. Information to the public + +In case of any Distribution or Communication of the Work by means of electronic +communication by You (for example, by offering to download the Work from a +remote location) the distribution channel or media (for example, a website) must +at least provide to the public the information requested by the applicable law +regarding the Licensor, the Licence and the way it may be accessible, concluded, +stored and reproduced by the Licensee. + +12. Termination of the Licence + +The Licence and the rights granted hereunder will terminate automatically upon +any breach by the Licensee of the terms of the Licence. + +Such a termination will not terminate the licences of any person who has +received the Work from the Licensee under the Licence, provided such persons +remain in full compliance with the Licence. + +13. Miscellaneous + +Without prejudice of Article 9 above, the Licence represents the complete +agreement between the Parties as to the Work. + +If any provision of the Licence is invalid or unenforceable under applicable +law, this will not affect the validity or enforceability of the Licence as a +whole. Such provision will be construed or reformed so as necessary to make it +valid and enforceable. + +The European Commission may publish other linguistic versions or new versions of +this Licence or updated versions of the Appendix, so far this is required and +reasonable, without reducing the scope of the rights granted by the Licence. New +versions of the Licence will be published with a unique version number. + +All linguistic versions of this Licence, approved by the European Commission, +have identical value. Parties can take advantage of the linguistic version of +their choice. + +14. Jurisdiction + +Without prejudice to specific agreement between parties, + +- any litigation resulting from the interpretation of this License, arising + between the European Union institutions, bodies, offices or agencies, as a + Licensor, and any Licensee, will be subject to the jurisdiction of the Court + of Justice of the European Union, as laid down in article 272 of the Treaty on + the Functioning of the European Union, + +- any litigation arising between other parties and resulting from the + interpretation of this License, will be subject to the exclusive jurisdiction + of the competent court where the Licensor resides or conducts its primary + business. + +15. Applicable Law + +Without prejudice to specific agreement between parties, + +- this Licence shall be governed by the law of the European Union Member State + where the Licensor has his seat, resides or has his registered office, + +- this licence shall be governed by Belgian law if the Licensor has no seat, + residence or registered office inside a European Union Member State. + +Appendix + +‘Compatible Licences’ according to Article 5 EUPL are: + +- GNU General Public License (GPL) v. 2, v. 3 +- GNU Affero General Public License (AGPL) v. 3 +- Open Software License (OSL) v. 2.1, v. 3.0 +- Eclipse Public License (EPL) v. 1.0 +- CeCILL v. 2.0, v. 2.1 +- Mozilla Public Licence (MPL) v. 2 +- GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3 +- Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for + works other than software +- European Union Public Licence (EUPL) v. 1.1, v. 1.2 +- Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong + Reciprocity (LiLiQ-R+). + +The European Commission may update this Appendix to later versions of the above +licences without producing a new version of the EUPL, as long as they provide +the rights granted in Article 2 of this Licence and protect the covered Source +Code from exclusive appropriation. + +All other changes or additions to this Appendix require the production of a new +EUPL version. diff --git a/crates/tytix-core/Cargo.toml b/crates/tytix-core/Cargo.toml index 071be85..3edc627 100644 --- a/crates/tytix-core/Cargo.toml +++ b/crates/tytix-core/Cargo.toml @@ -5,13 +5,14 @@ edition.workspace = true authors.workspace = true description = "A clear and simple to use actor framework" -repository = "https://github.com/TheNeikos/tytix" -license = "EUPL-1.2" -keywords = [] -categories = [] +repository.workspace = true +license.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +anyhow = "1" [dev-dependencies] +macro_rules_attribute = "0.2" +smol-macros = "0.1" diff --git a/crates/tytix-core/src/lib.rs b/crates/tytix-core/src/lib.rs index 2f9d629..7651dee 100644 --- a/crates/tytix-core/src/lib.rs +++ b/crates/tytix-core/src/lib.rs @@ -4,6 +4,8 @@ use std::any::Any; use std::any::TypeId; +pub use anyhow; + pub trait Message: Send + Any { type Reply: Send + Any; } @@ -16,43 +18,78 @@ impl MessageIdentifier for M { const IDENT: BundleChain = BundleChain::of::(); } -pub struct InternalMessage(Box); +pub struct InternalMessage { + value: Box, + name: &'static str, +} impl InternalMessage { - pub fn new(message: impl Message) -> InternalMessage { - InternalMessage(Box::new(message)) + pub fn new(message: M) -> InternalMessage { + InternalMessage { + value: Box::new(message), + name: std::any::type_name::(), + } } - pub fn into_inner(self) -> Result { - self.0.downcast().map(|v| *v).map_err(InternalMessage) + pub fn into_inner(self) -> Result { + self.value + .downcast() + .map(|v| *v) + .map_err(|value| InternalMessage { + value, + name: self.name, + }) + } + + pub fn as_ref(&self) -> Option<&M> { + self.value.downcast_ref() + } + + pub fn type_name(&self) -> &'static str { + self.name + } + + pub fn type_id(&self) -> TypeId { + self.value.as_ref().type_id() } } -pub trait Address -where - MB: MessageBundle, -{ - fn send(&mut self, message: M); +pub trait Address { + fn send(&mut self, message: M) -> impl Future>; } pub trait InternalMessageHandler { type HandledMessages: MessageBundle; - fn handle_message(&mut self, msg: InternalMessage); + fn handle_message( + &mut self, + msg: InternalMessage, + ) -> impl Future>; } -impl Address for IMH +impl Address for IMH where - MB: MessageBundle, - IMH: InternalMessageHandler, + IMH: InternalMessageHandler, { - fn send(&mut self, message: M) { + fn send(&mut self, message: M) -> impl Future> { const { - let true = >::IS_CONTAINED else { + let true = >::IS_CONTAINED else { panic!("Message is not contained in MessageBundle",); }; } - self.handle_message(InternalMessage(Box::new(message))); + let message = self.handle_message(InternalMessage::new(message)); + + async { + message.await.and_then(|msg| { + msg.into_inner::().map_err(|e| { + anyhow::anyhow!( + "Expected a {}, but got a {}", + std::any::type_name::(), + e.type_name() + ) + }) + }) + } } } @@ -83,6 +120,7 @@ pub struct BundleChain { pub enum BundleOp { Add(TypeId), Remove(TypeId), + Chain(&'static BundleChain), } impl BundleChain { @@ -93,12 +131,31 @@ impl BundleChain { } } + pub const fn contains(&self, id: TypeId) -> bool { + check_is_contained(self, id) + } + pub const fn with(&'static self) -> BundleChain { - add_to_chain(self, TypeId::of::()) + let to_add = TypeId::of::(); + BundleChain { + op: BundleOp::Add(to_add), + next: Some(self), + } } pub const fn without(&'static self) -> BundleChain { - remove_from_chain(self, TypeId::of::()) + let to_remove = TypeId::of::(); + BundleChain { + op: BundleOp::Remove(to_remove), + next: Some(self), + } + } + + pub const fn join(&'static self, ids: &'static BundleChain) -> BundleChain { + BundleChain { + op: BundleOp::Chain(self), + next: Some(ids), + } } } @@ -132,7 +189,7 @@ where const IS_CONTAINED: bool = check_is_contained(&MB::IDS, TypeId::of::()); } -const fn check_is_contained(ids: &'static BundleChain, id: TypeId) -> bool { +const fn check_is_contained(ids: &BundleChain, id: TypeId) -> bool { match ids.op { BundleOp::Add(added_id) => { if check_type_id_equal(added_id, id) { @@ -144,6 +201,11 @@ const fn check_is_contained(ids: &'static BundleChain, id: TypeId) -> bool { return false; } } + BundleOp::Chain(other_chain) => { + if check_is_contained(other_chain, id) { + return true; + } + } } if let Some(next) = ids.next { @@ -157,22 +219,11 @@ const fn check_type_id_equal(left: TypeId, right: TypeId) -> bool { left == right } -const fn add_to_chain(prev: &'static BundleChain, to_add: TypeId) -> BundleChain { - BundleChain { - op: BundleOp::Add(to_add), - next: Some(prev), - } -} - -const fn remove_from_chain(prev: &'static BundleChain, to_remove: TypeId) -> BundleChain { - BundleChain { - op: BundleOp::Remove(to_remove), - next: Some(prev), - } -} - #[cfg(test)] mod tests { + use macro_rules_attribute::apply; + use smol_macros::test; + use super::*; struct Foo; @@ -218,18 +269,23 @@ mod tests { )); } - #[test] - fn check_sending_messages() { + #[apply(test!)] + async fn check_sending_messages() { struct Sender; impl InternalMessageHandler for Sender { type HandledMessages = (Foo, Bar); - fn handle_message(&mut self, _msg: InternalMessage) {} + async fn handle_message( + &mut self, + _msg: InternalMessage, + ) -> anyhow::Result { + Err(anyhow::anyhow!("Not implemented!")) + } } let mut s = Sender; - s.send(Foo); + s.send(Foo).await.unwrap_err(); } } diff --git a/crates/tytix/Cargo.toml b/crates/tytix/Cargo.toml index e7e8af6..36cbaec 100644 --- a/crates/tytix/Cargo.toml +++ b/crates/tytix/Cargo.toml @@ -1,5 +1,7 @@ [package] name = "tytix" +license.workspace = true +repository.workspace = true version.workspace = true edition.workspace = true authors.workspace = true @@ -7,6 +9,9 @@ authors.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +futures = "0.3.31" tytix-core = { path = "../tytix-core/" } [dev-dependencies] +macro_rules_attribute = "0.2.2" +smol-macros = "0.1.1" diff --git a/crates/tytix/src/lib.rs b/crates/tytix/src/lib.rs index 5b3512b..f1222ef 100644 --- a/crates/tytix/src/lib.rs +++ b/crates/tytix/src/lib.rs @@ -1,48 +1,141 @@ use std::marker::PhantomData; -use tytix_core::Address; +use futures::FutureExt; +use tytix_core::InternalMessage; use tytix_core::InternalMessageHandler; use tytix_core::IsContainedInBundle; use tytix_core::Message; use tytix_core::MessageBundle; +use tytix_core::anyhow; + +pub type ReplyOf = ::Reply; pub trait AddressExt { - fn map_before(self, f: F) -> MappedBeforeAddress + fn map(self, f: F, r: RF) -> MappedMessage where - MB: MessageBundle, - F: Fn(M) -> R + 'static, - M: Message, - R: Message + IsContainedInBundle, + MappedMessage: InternalMessageHandler, + Self: Sized; + + fn inspect(self, f: F) -> Inspect + where + F: Fn(&M) -> U, + M: Message + IsContainedInBundle, + Self: Sized; + + fn join(self, o: Other) -> Joined + where + Joined: InternalMessageHandler, Self: Sized; } -impl> AddressExt for A { - fn map_before(self, f: F) -> MappedBeforeAddress +impl> AddressExt for A { + fn map(self, f: F, r: RF) -> MappedMessage where - MB: MessageBundle, - F: Fn(M) -> R + 'static, - M: Message, - R: Message + IsContainedInBundle, + MappedMessage: InternalMessageHandler, Self: Sized, + { + MappedMessage { + address: self, + func: f, + reply: r, + _pd: PhantomData, + } + } + + fn inspect(self, f: F) -> Inspect + where + F: Fn(&M) -> U, + Self: Sized, + M: Message + IsContainedInBundle, { const { - let true = >::IS_CONTAINED else { + let true = >::IS_CONTAINED else { panic!("Message is not contained in MessageBundle",); }; } - MappedBeforeAddress { + Inspect { address: self, func: f, _pd: PhantomData, } } + + fn join(self, o: Other) -> Joined + where + Joined: InternalMessageHandler, + Self: Sized, + { + Joined { + left: self, + right: o, + } + } } -pub struct MappedBeforeAddress { +pub struct Joined { + left: L, + right: R, +} + +pub struct JoinedMessages { + _pd: PhantomData, +} + +impl MessageBundle for JoinedMessages +where + L: MessageBundle, + R: MessageBundle, +{ + const IDS: tytix_core::BundleChain = ::IDS.join(&::IDS); +} + +impl InternalMessageHandler for Joined +where + L: InternalMessageHandler, + R: InternalMessageHandler, +{ + type HandledMessages = JoinedMessages; + + async fn handle_message(&mut self, msg: InternalMessage) -> anyhow::Result { + let message_id = msg.type_id(); + + if L::HandledMessages::IDS.contains(message_id) { + self.left.handle_message(msg).await + } else { + self.right.handle_message(msg).await + } + } +} + +pub struct Inspect { address: A, func: F, - _pd: PhantomData R>, + _pd: PhantomData U>, +} + +impl InternalMessageHandler for Inspect +where + A: InternalMessageHandler, + M: Message, + F: Fn(&M) -> U, + U: Future, +{ + type HandledMessages = A::HandledMessages; + async fn handle_message(&mut self, msg: InternalMessage) -> anyhow::Result { + if let Some(msg) = msg.as_ref() { + (self.func)(msg).await; + } + + self.address.handle_message(msg).await + } +} + +pub struct MappedMessage { + address: A, + func: F, + reply: RF, + _pd: PhantomData (U, RU)>, } pub struct MappedHandledMessages { @@ -58,23 +151,42 @@ where const IDS: tytix_core::BundleChain = MB::IDS.without::().with::(); } -impl InternalMessageHandler for MappedBeforeAddress +impl InternalMessageHandler for MappedMessage where A: InternalMessageHandler, M: Message, R: Message + IsContainedInBundle, - F: FnMut(M) -> R, + F: FnMut(M) -> U, + U: Future, + RF: FnMut(::Reply) -> RU, + RU: Future::Reply>, { type HandledMessages = MappedHandledMessages; - fn handle_message(&mut self, msg: tytix_core::InternalMessage) { - match msg.into_inner::() { - Ok(removed_message) => { - let new_message = (self.func)(removed_message); - - self.address.send(new_message); + fn handle_message( + &mut self, + msg: tytix_core::InternalMessage, + ) -> impl Future> { + let msg = match msg.into_inner::() { + Ok(incoming) => { + let map_fut = (self.func)(incoming); + async { InternalMessage::new(map_fut.await) }.left_future() } - Err(other_message) => self.address.handle_message(other_message), + Err(other) => async { other }.right_future(), + }; + + async { + let msg = msg.await; + + let msg_reply = self.address.handle_message(msg).await?; + + let Ok(msg_reply) = msg_reply.into_inner() else { + return Err(anyhow::anyhow!("Internal Error: Received the wrong type")); + }; + + let msg_reply = (self.reply)(msg_reply).await; + + Ok(InternalMessage::new(msg_reply)) } } } @@ -83,12 +195,16 @@ where mod tests { use std::sync::OnceLock; + use macro_rules_attribute::apply; + use smol_macros::test; + use tytix_core::Address; + use super::*; struct Foo; impl Message for Foo { - type Reply = (); + type Reply = usize; } struct Bar; @@ -97,27 +213,99 @@ mod tests { type Reply = (); } + struct Zap; + + impl Message for Zap { + type Reply = usize; + } + struct SimpleAddress; impl InternalMessageHandler for SimpleAddress { - type HandledMessages = (Foo,); + type HandledMessages = (Foo, Bar); - fn handle_message(&mut self, msg: tytix_core::InternalMessage) { - drop(msg); + async fn handle_message( + &mut self, + msg: tytix_core::InternalMessage, + ) -> anyhow::Result { + if let Ok(_foo) = msg.into_inner::() { + Ok(InternalMessage::new(42usize)) + } else { + Ok(InternalMessage::new(())) + } } } - #[test] - fn check_mapping() { + struct ZapAddress; + + impl InternalMessageHandler for ZapAddress { + type HandledMessages = (Zap,); + + async fn handle_message( + &mut self, + _msg: InternalMessage, + ) -> anyhow::Result { + Ok(InternalMessage::new(45usize)) + } + } + + #[apply(test!)] + async fn check_mapping() { static MSG: OnceLock = OnceLock::new(); - let mut sa = SimpleAddress.map_before(|_b: Bar| { - let _ = MSG.set(true); - Foo - }); + let mut sa = SimpleAddress.map( + |_b: Bar| { + let _ = MSG.set(true); + async { Foo } + }, + |_a| async {}, + ); - sa.send(Bar); + let () = sa.send(Bar).await.unwrap(); MSG.get().expect("The message was mapped!"); } + + #[apply(test!)] + async fn check_inspect() { + static MSG: OnceLock = OnceLock::new(); + + let mut sa = SimpleAddress.inspect(|_b: &Bar| { + let _ = MSG.set(true); + async {} + }); + + sa.send(Foo).await.unwrap(); + + assert!(MSG.get().is_none()); + + sa.send(Bar).await.unwrap(); + + MSG.get().expect("The message was inspected!"); + } + + #[apply(test!)] + async fn check_join() { + static MSG_SA: OnceLock = OnceLock::new(); + static MSG_ZAP: OnceLock = OnceLock::new(); + + let sa = SimpleAddress.inspect(|_b: &Bar| { + MSG_SA.set(true).unwrap(); + async {} + }); + + let zap = ZapAddress.inspect(|_z: &Zap| async { + MSG_ZAP.set(true).unwrap(); + }); + + let mut joined = sa.join(zap); + + joined.send(Bar).await.unwrap(); + + MSG_SA.get().expect("The message was not :CC inspected!"); + + joined.send(Zap).await.unwrap(); + + MSG_ZAP.get().expect("The message was NOT inspected!"); + } }