From 8f749e06d7c780bca5cb80d60c2d080e5f9dbbba Mon Sep 17 00:00:00 2001 From: Daniel Thaler Date: Sat, 19 Aug 2023 00:43:38 +0200 Subject: [PATCH] Initial commit: First draft --- .github/workflows/CI.yml | 120 ++++++ .gitignore | 74 ++++ Cargo.lock | 334 +++++++++++++++ Cargo.toml | 13 + LICENSE-APACHE | 201 +++++++++ LICENSE-MIT | 21 + README.md | 24 ++ pyproject.toml | 16 + src/lib.rs | 861 +++++++++++++++++++++++++++++++++++++++ 9 files changed, 1664 insertions(+) create mode 100644 .github/workflows/CI.yml create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 LICENSE-APACHE create mode 100644 LICENSE-MIT create mode 100644 README.md create mode 100644 pyproject.toml create mode 100644 src/lib.rs diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml new file mode 100644 index 0000000..64a4f48 --- /dev/null +++ b/.github/workflows/CI.yml @@ -0,0 +1,120 @@ +# This file is autogenerated by maturin v1.2.0 +# To update, run +# +# maturin generate-ci github -o .\.github\workflows\CI.yml +# +name: CI + +on: + push: + branches: + - main + - master + tags: + - '*' + pull_request: + workflow_dispatch: + +permissions: + contents: read + +jobs: + linux: + runs-on: ubuntu-latest + strategy: + matrix: + target: [x86_64, x86, aarch64, armv7, s390x, ppc64le] + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: '3.10' + - name: Build wheels + uses: PyO3/maturin-action@v1 + with: + target: ${{ matrix.target }} + args: --release --out dist --find-interpreter + sccache: 'true' + manylinux: auto + - name: Upload wheels + uses: actions/upload-artifact@v3 + with: + name: wheels + path: dist + + windows: + runs-on: windows-latest + strategy: + matrix: + target: [x64, x86] + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: '3.10' + architecture: ${{ matrix.target }} + - name: Build wheels + uses: PyO3/maturin-action@v1 + with: + target: ${{ matrix.target }} + args: --release --out dist --find-interpreter + sccache: 'true' + - name: Upload wheels + uses: actions/upload-artifact@v3 + with: + name: wheels + path: dist + + macos: + runs-on: macos-latest + strategy: + matrix: + target: [x86_64, aarch64] + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: '3.10' + - name: Build wheels + uses: PyO3/maturin-action@v1 + with: + target: ${{ matrix.target }} + args: --release --out dist --find-interpreter + sccache: 'true' + - name: Upload wheels + uses: actions/upload-artifact@v3 + with: + name: wheels + path: dist + + sdist: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Build sdist + uses: PyO3/maturin-action@v1 + with: + command: sdist + args: --out dist + - name: Upload sdist + uses: actions/upload-artifact@v3 + with: + name: wheels + path: dist + + release: + name: Release + runs-on: ubuntu-latest + if: "startsWith(github.ref, 'refs/tags/')" + needs: [linux, windows, macos, sdist] + steps: + - uses: actions/download-artifact@v3 + with: + name: wheels + - name: Publish to PyPI + uses: PyO3/maturin-action@v1 + env: + MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_API_TOKEN }} + with: + command: upload + args: --skip-existing * diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4bf34a6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,74 @@ +/target + +# Byte-compiled / optimized / DLL files +__pycache__/ +.pytest_cache/ +*.py[cod] + +# C extensions +*.so +*.dll + +# Distribution / packaging +.Python +.venv/ +.env/ +env/ +bin/ +build/ +develop-eggs/ +dist/ +eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +include/ +man/ +venv/ +*.egg-info/ +.installed.cfg +*.egg + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt +pip-selfcheck.json + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.cache +nosetests.xml +coverage.xml + +# Translations +*.mo + +# Mr Developer +.mr.developer.cfg +.project +.pydevproject + +# Rope +.ropeproject + +# Django stuff: +*.log +*.pot + +.DS_Store + +# Sphinx documentation +docs/_build/ + +# PyCharm +.idea/ + +# VSCode +.vscode/ + +# Pyenv +.python-version \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..4c64595 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,334 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "autosar-data" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9db9bccd1510ba22eeb8cc76e71c04ebd1d400d37679f0e1e88955bc1fad036" +dependencies = [ + "autosar-data-specification", + "parking_lot", + "rustc-hash", + "smallvec", + "thiserror", +] + +[[package]] +name = "autosar-data-specification" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a49cef1cb790fc97f6e3816c8232b41689a1a7b6be3265d93ab6e85fca3df292" +dependencies = [ + "pyo3", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "indoc" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa799dd5ed20a7e349f3b4639aa80d74549c81716d9ec4f994c9b5815598306" + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "lock_api" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "proc-macro2" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "py-autosar-data" +version = "0.1.0" +dependencies = [ + "autosar-data", + "autosar-data-specification", + "pyo3", +] + +[[package]] +name = "pyo3" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e681a6cfdc4adcc93b4d3cf993749a4552018ee0a9b65fc0ccfad74352c72a38" +dependencies = [ + "cfg-if", + "indoc", + "libc", + "memoffset", + "parking_lot", + "pyo3-build-config", + "pyo3-ffi", + "pyo3-macros", + "unindent", +] + +[[package]] +name = "pyo3-build-config" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "076c73d0bc438f7a4ef6fdd0c3bb4732149136abd952b110ac93e4edb13a6ba5" +dependencies = [ + "once_cell", + "target-lexicon", +] + +[[package]] +name = "pyo3-ffi" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e53cee42e77ebe256066ba8aa77eff722b3bb91f3419177cf4cd0f304d3284d9" +dependencies = [ + "libc", + "pyo3-build-config", +] + +[[package]] +name = "pyo3-macros" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfeb4c99597e136528c6dd7d5e3de5434d1ceaf487436a3f03b2d56b6fc9efd1" +dependencies = [ + "proc-macro2", + "pyo3-macros-backend", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "pyo3-macros-backend" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "947dc12175c254889edc0c02e399476c2f652b4b9ebd123aa655c224de259536" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "smallvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "target-lexicon" +version = "0.12.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a" + +[[package]] +name = "thiserror" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.29", +] + +[[package]] +name = "unicode-ident" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" + +[[package]] +name = "unindent" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1766d682d402817b5ac4490b3c3002d91dfa0d22812f341609f97b08757359c" + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..716d940 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "py-autosar-data" +version = "0.1.0" +edition = "2021" + +[lib] +name = "autosar_data" +crate-type = ["cdylib"] + +[dependencies] +autosar-data = {version = "0.7"} +autosar-data-specification = {version = "0.7", features = ["pylib"]} +pyo3 = "0.19.2" diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,201 @@ + 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/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 0000000..bd1fb49 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2021 Daniel Thaler + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..78a3b91 --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +# `autosar-data-py` + +Python bindings for [autosar-data](https://github.com/DanielT/autosar-data) + +## Features + +This crate implements Python bindings for autosar-data using [PyO3](https://pyo3.rs). This allows all the features of `autosar-data` to be used from python code: + +- read and write arxml files +- fully validate all data when it is loaded in strict mode +- non-strict mode so that invalid but structurally sound data can be loaded +- various element operations to modify and create sub-elements, data and attributes +- support for Autosar paths and cross references +- supports Autosar version 4.0.1 and up. + +## Example + +```python +from autosar_data import * + +# create a new data model +model = AutosarModel() + +``` diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..5fb78ba --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,16 @@ +[build-system] +requires = ["maturin>=1.2,<2.0"] +build-backend = "maturin" + +[project] +name = "py-autosar-data" +requires-python = ">=3.7" +classifiers = [ + "Programming Language :: Rust", + "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy", +] + + +[tool.maturin] +features = ["pyo3/extension-module"] diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..93ca690 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,861 @@ +use std::collections::{HashMap, HashSet}; + +use ::autosar_data as autosar_data_rs; +use autosar_data_rs::CompatibilityError; +use pyo3::create_exception; +use pyo3::prelude::*; +use pyo3::types::*; + +create_exception!(module, AutosarDataError, pyo3::exceptions::PyException); + +#[pyclass(frozen)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +struct AutosarModel(autosar_data_rs::AutosarModel); + +#[pyclass(frozen)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +struct ArxmlFile(autosar_data_rs::ArxmlFile); + +#[pyclass(frozen)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +struct Element(autosar_data_rs::Element); + +#[pyclass] +struct ElementsDfsIterator(autosar_data_rs::ElementsDfsIterator); + +#[pyclass] +struct ArxmlFileElementsDfsIterator(autosar_data_rs::ArxmlFileElementsDfsIterator); + +#[pyclass] +struct ElementContentIterator(autosar_data_rs::ElementContentIterator); + +#[pyclass] +struct ElementsIterator(autosar_data_rs::ElementsIterator); + +#[pyclass] +struct AttributeIterator(autosar_data_rs::AttributeIterator); + +#[pyclass(frozen)] +#[derive(Debug, Clone, PartialEq, Eq)] +struct IncompatibleElementError { + element: Element, + version_mask: u32, + target_version: autosar_data_rs::AutosarVersion, +} + +#[pyclass(frozen)] +#[derive(Debug, Clone, PartialEq, Eq)] +struct IncompatibleAttributeError { + element: Element, + attribute: autosar_data_rs::AttributeName, + version_mask: u32, + target_version: autosar_data_rs::AutosarVersion, +} + +#[pyclass(frozen)] +#[derive(Debug, Clone, PartialEq, Eq)] +struct IncompatibleAttributeValueError { + element: Element, + attribute: autosar_data_rs::AttributeName, + attribute_value: String, + version_mask: u32, + target_version: autosar_data_rs::AutosarVersion, +} + +#[pyclass(frozen)] +#[derive(Clone, PartialEq, Eq)] +struct ElementType(autosar_data_specification::ElementType); + +#[pyclass(frozen)] +struct Attribute { + pub attrname: autosar_data_rs::AttributeName, + pub content: PyObject, +} + +#[pyclass(frozen)] +#[derive(Debug, Clone, PartialEq, Eq)] +enum ContentType { + /// The element only contains other elements + Elements, + /// The element only contains character data + CharacterData, + /// The element contains both character data and sub elements + Mixed, +} + +#[pymethods] +impl AutosarModel { + #[new] + fn new() -> Self { + Self(autosar_data_rs::AutosarModel::new()) + } + + fn __repr__(&self) -> String { + format!("{:#?}", self.0) + } + + fn __str__(&self) -> String { + self.0.root_element().serialize() + } + + fn create_file(&self, filename: &str, version: autosar_data_rs::AutosarVersion) -> PyResult { + match self.0.create_file(filename, version) { + Ok(file) => Ok(ArxmlFile(file)), + Err(error) => PyResult::Err(AutosarDataError::new_err(error.to_string())), + } + } + + fn load_buffer(&self, buffer: &str, filename: &str, strict: bool) -> PyResult<(ArxmlFile, Vec)> { + match self.0.load_named_arxml_buffer(buffer.as_bytes(), filename, strict) { + Ok((file, warn)) => { + let warnstrings: Vec = warn.iter().map(|w| w.to_string()).collect(); + Ok((ArxmlFile(file), warnstrings)) + } + Err(error) => PyResult::Err(AutosarDataError::new_err(error.to_string())), + } + } + + fn load_file(&self, filename: &str, strict: bool) -> PyResult<(ArxmlFile, Vec)> { + match self.0.load_arxml_file(filename, strict) { + Ok((file, warn)) => { + let warnstrings: Vec = warn.iter().map(|w| w.to_string()).collect(); + Ok((ArxmlFile(file), warnstrings)) + } + Err(error) => PyResult::Err(AutosarDataError::new_err(error.to_string())), + } + } + + fn remove_file(&self, file: &ArxmlFile) { + self.0.remove_file(&file.0); + } + + fn serialize_files(&self) -> HashMap { + let hm_orig: HashMap = self.0.serialize_files(); + let mut hm_out = HashMap::::new(); + for (k, v) in hm_orig { + hm_out.insert(String::from(k.to_string_lossy()), v); + } + hm_out + } + + fn write(&self) -> PyResult<()> { + self.0 + .write() + .map_err(|error| AutosarDataError::new_err(error.to_string())) + } + + #[getter] + fn files(&self) -> Vec { + self.0.files().map(ArxmlFile).collect() + } + + #[getter] + fn root_element(&self) -> Element { + Element(self.0.root_element()) + } + + fn get_element_by_path(&self, path: &str) -> Option { + self.0.get_element_by_path(path).map(Element) + } + + #[getter] + fn elements_dfs(&self) -> ElementsDfsIterator { + ElementsDfsIterator(self.0.elements_dfs()) + } + + fn sort(&self) { + self.0.sort() + } + + #[getter] + fn identifiable_elements(&self) -> Vec { + self.0.identifiable_elements() + } + + fn get_references_to(&self, target_path: &str) -> Vec { + self.0 + .get_references_to(target_path) + .iter() + .filter_map(|weak| weak.upgrade().map(Element)) + .collect() + } + + fn check_references(&self) -> Vec { + self.0 + .check_references() + .iter() + .filter_map(|weak| weak.upgrade().map(Element)) + .collect() + } +} + +#[pymethods] +impl ArxmlFile { + fn __repr__(&self) -> String { + format!("{:#?}", self.0) + } + + fn __str__(&self) -> PyResult { + self.serialize() + } + + #[getter] + fn filename(&self) -> String { + self.0.filename().to_string_lossy().into_owned() + } + + #[setter] + fn set_filename(&self, filename: &str) -> PyResult<()> { + self.0 + .set_filename(filename) + .map_err(|error| AutosarDataError::new_err(error.to_string())) + } + + #[getter] + fn version(&self) -> autosar_data_rs::AutosarVersion { + self.0.version() + } + + #[setter] + fn set_version(&self, version: autosar_data_rs::AutosarVersion) -> PyResult<()> { + self.0 + .set_version(version) + .map_err(|error| AutosarDataError::new_err(error.to_string())) + } + + fn check_version_compatibility(&self, target_version: autosar_data_rs::AutosarVersion) -> Vec { + Python::with_gil(|py| { + self.0 + .check_version_compatibility(target_version) + .0 + .iter() + .map(|cerr| -> PyObject { + match cerr { + CompatibilityError::IncompatibleAttribute { + element, + attribute, + version_mask, + } => Py::new( + py, + IncompatibleAttributeError { + element: Element(element.to_owned()), + attribute: *attribute, + version_mask: *version_mask, + target_version, + }, + ) + .unwrap() + .into_py(py), + CompatibilityError::IncompatibleAttributeValue { + element, + attribute, + attribute_value, + version_mask, + } => Py::new( + py, + IncompatibleAttributeValueError { + element: Element(element.to_owned()), + attribute: *attribute, + attribute_value: attribute_value.to_owned(), + version_mask: *version_mask, + target_version, + }, + ) + .unwrap() + .into_py(py), + CompatibilityError::IncompatibleElement { element, version_mask } => Py::new( + py, + IncompatibleElementError { + element: Element(element.to_owned()), + version_mask: *version_mask, + target_version, + }, + ) + .unwrap() + .into_py(py), + } + }) + .collect() + }) + } + + #[getter] + fn model(&self) -> PyResult { + match self.0.model() { + Ok(model) => Ok(AutosarModel(model)), + Err(error) => PyResult::Err(AutosarDataError::new_err(error.to_string())), + } + } + + #[getter] + fn elements_dfs(&self) -> ArxmlFileElementsDfsIterator { + ArxmlFileElementsDfsIterator(self.0.elements_dfs()) + } + + fn serialize(&self) -> PyResult { + match self.0.serialize() { + Ok(text) => Ok(text), + Err(error) => Err(AutosarDataError::new_err(error.to_string())), + } + } + + #[getter] + fn xml_standalone(&self) -> Option { + self.0.xml_standalone() + } +} + +#[pymethods] +impl Element { + fn __repr__(&self) -> String { + format!("{:#?}", self.0) + } + + fn __str__(&self) -> String { + self.0.serialize() + } + + fn serialize(&self) -> String { + self.0.serialize() + } + + #[getter] + fn parent(&self) -> PyResult> { + match self.0.parent() { + Ok(Some(parent)) => Ok(Some(Element(parent))), + Ok(None) => Ok(None), + Err(error) => Err(AutosarDataError::new_err(error.to_string())), + } + } + + #[getter] + fn element_name(&self) -> autosar_data_rs::ElementName { + self.0.element_name() + } + + #[getter] + fn element_type(&self) -> ElementType { + ElementType(self.0.element_type()) + } + + #[getter] + fn item_name(&self) -> Option { + self.0.item_name() + } + + #[setter] + fn set_item_name(&self, new_name: &str) -> PyResult<()> { + match self.0.set_item_name(new_name) { + Ok(()) => Ok(()), + Err(error) => Err(AutosarDataError::new_err(error.to_string())), + } + } + + #[getter] + fn is_identifiable(&self) -> bool { + self.0.is_identifiable() + } + + #[getter] + fn is_reference(&self) -> bool { + self.0.element_type().is_ref() + } + + #[getter] + fn path(&self) -> PyResult { + match self.0.path() { + Ok(path) => Ok(path), + Err(error) => Err(AutosarDataError::new_err(error.to_string())), + } + } + + #[getter] + fn model(&self) -> PyResult { + match self.0.model() { + Ok(model) => Ok(AutosarModel(model)), + Err(error) => Err(AutosarDataError::new_err(error.to_string())), + } + } + + #[getter] + fn content_type(&self) -> ContentType { + match self.0.content_type() { + autosar_data_rs::ContentType::Elements => ContentType::Elements, + autosar_data_rs::ContentType::CharacterData => ContentType::CharacterData, + autosar_data_rs::ContentType::Mixed => ContentType::Mixed, + } + } + + fn create_sub_element(&self, element_name: autosar_data_rs::ElementName) -> PyResult { + match self.0.create_sub_element(element_name) { + Ok(element) => Ok(Element(element)), + Err(error) => Err(AutosarDataError::new_err(error.to_string())), + } + } + + fn create_sub_element_at( + &self, + element_name: autosar_data_rs::ElementName, + position: usize, + ) -> PyResult { + match self.0.create_sub_element_at(element_name, position) { + Ok(element) => Ok(Element(element)), + Err(error) => Err(AutosarDataError::new_err(error.to_string())), + } + } + + fn create_named_sub_element( + &self, + element_name: autosar_data_rs::ElementName, + item_name: &str, + ) -> PyResult { + match self.0.create_named_sub_element(element_name, item_name) { + Ok(element) => Ok(Element(element)), + Err(error) => Err(AutosarDataError::new_err(error.to_string())), + } + } + + fn create_named_sub_element_at( + &self, + element_name: autosar_data_rs::ElementName, + item_name: &str, + position: usize, + ) -> PyResult { + match self.0.create_named_sub_element_at(element_name, item_name, position) { + Ok(element) => Ok(Element(element)), + Err(error) => Err(AutosarDataError::new_err(error.to_string())), + } + } + + fn create_copied_sub_element(&self, other: &Element) -> PyResult { + match self.0.create_copied_sub_element(&other.0) { + Ok(element) => Ok(Element(element)), + Err(error) => Err(AutosarDataError::new_err(error.to_string())), + } + } + + fn create_copied_sub_element_at(&self, other: &Element, position: usize) -> PyResult { + match self.0.create_copied_sub_element_at(&other.0, position) { + Ok(element) => Ok(Element(element)), + Err(error) => Err(AutosarDataError::new_err(error.to_string())), + } + } + + fn move_element_here(&self, move_element: &Element) -> PyResult { + match self.0.move_element_here(&move_element.0) { + Ok(element) => Ok(Element(element)), + Err(error) => Err(AutosarDataError::new_err(error.to_string())), + } + } + + fn move_element_here_at(&self, move_element: &Element, position: usize) -> PyResult { + match self.0.move_element_here_at(&move_element.0, position) { + Ok(element) => Ok(Element(element)), + Err(error) => Err(AutosarDataError::new_err(error.to_string())), + } + } + + fn remove_sub_element(&self, sub_element: Element) -> PyResult<()> { + self.0 + .remove_sub_element(sub_element.0) + .map_err(|error| AutosarDataError::new_err(error.to_string())) + } + + #[setter] + fn set_reference_target(&self, target: Element) -> PyResult<()> { + self.0 + .set_reference_target(&target.0) + .map_err(|error| AutosarDataError::new_err(error.to_string())) + } + + #[getter] + fn get_reference_target(&self) -> PyResult { + match self.0.get_reference_target() { + Ok(target) => Ok(Element(target)), + Err(error) => Err(AutosarDataError::new_err(error.to_string())), + } + } + + fn get_sub_element(&self, name: autosar_data_rs::ElementName) -> Option { + self.0.get_sub_element(name).map(Element) + } + + #[getter] + fn sub_elements(&self) -> ElementsIterator { + ElementsIterator(self.0.sub_elements()) + } + + #[getter] + fn elements_dfs(&self) -> ElementsDfsIterator { + ElementsDfsIterator(self.0.elements_dfs()) + } + + #[setter] + fn set_character_data(&self, chardata: PyObject) -> PyResult<()> { + let cdata = extract_character_data(chardata)?; + self.0 + .set_character_data(cdata) + .map_err(|error| AutosarDataError::new_err(error.to_string())) + } + + fn remove_character_data(&self) -> PyResult<()> { + self.0 + .remove_character_data() + .map_err(|error| AutosarDataError::new_err(error.to_string())) + } + + #[getter] + fn character_data(&self) -> Option { + self.0.character_data().map(|cdata| character_data_to_object(&cdata)) + } + + fn insert_character_content_item(&self, chardata: &str, position: usize) -> PyResult<()> { + self.0 + .insert_character_content_item(chardata, position) + .map_err(|error| AutosarDataError::new_err(error.to_string())) + } + + fn remove_character_content_item(&self, position: usize) -> PyResult<()> { + match self.0.remove_character_content_item(position) { + Ok(()) => Ok(()), + Err(error) => Err(AutosarDataError::new_err(error.to_string())), + } + } + + #[getter] + fn content(&self) -> ElementContentIterator { + ElementContentIterator(self.0.content()) + } + + #[getter] + fn attributes(&self) -> AttributeIterator { + AttributeIterator(self.0.attributes()) + } + + fn attribute_value(&self, attrname: autosar_data_rs::AttributeName) -> Option { + Some(character_data_to_object(&self.0.attribute_value(attrname)?)) + } + + fn set_attribute(&self, attrname: autosar_data_rs::AttributeName, value: PyObject) -> PyResult<()> { + let cdata = extract_character_data(value)?; + self.0 + .set_attribute(attrname, cdata) + .map_err(|error| AutosarDataError::new_err(error.to_string())) + } + + fn set_attribute_string(&self, attrname: autosar_data_rs::AttributeName, text: &str) -> PyResult<()> { + self.0 + .set_attribute_string(attrname, text) + .map_err(|error| AutosarDataError::new_err(error.to_string())) + } + + fn remove_attribute(&self, attrname: autosar_data_rs::AttributeName) -> bool { + self.0.remove_attribute(attrname) + } + + fn sort(&self) { + self.0.sort() + } + + fn list_valid_sub_elements(&self) -> Vec<(autosar_data_rs::ElementName, bool, bool)> { + self.0.list_valid_sub_elements() + } + + #[getter] + fn file_membership(&self) -> PyResult { + Python::with_gil(|py| match self.0.file_membership() { + Ok((local, weak_file_set)) => { + let file_set: Vec = weak_file_set + .iter() + .filter_map(|weak| { + weak.upgrade() + .map(|raw| Py::new(py, ArxmlFile(raw)).unwrap().into_py(py)) + }) + .collect(); + let frozenset: &PyFrozenSet = PyFrozenSet::new(py, file_set.iter()).unwrap(); + let pytuple: &PyTuple = PyTuple::new(py, [local.to_object(py), frozenset.to_object(py)].iter()); + Ok(pytuple.to_object(py)) + } + Err(error) => Err(AutosarDataError::new_err(error.to_string())), + }) + } + + fn set_file_membership(&self, file_membership: HashSet) { + self.0 + .set_file_membership(file_membership.iter().map(|weak| weak.0.downgrade()).collect()) + } + + fn add_to_file(&self, file: &ArxmlFile) -> PyResult<()> { + self.0 + .add_to_file(&file.0) + .map_err(|error| AutosarDataError::new_err(error.to_string())) + } + + fn remove_from_file(&self, file: &ArxmlFile) -> PyResult<()> { + self.0 + .remove_from_file(&file.0) + .map_err(|error| AutosarDataError::new_err(error.to_string())) + } + + #[getter] + fn xml_path(&self) -> String { + self.0.xml_path() + } +} + +#[pymethods] +impl IncompatibleAttributeError { + fn __repr__(&self) -> String { + format!("{:#?}", self) + } + + fn __str__(&self) -> String { + format!( + "Attribute {} in <{}> is incompatible with version {}", + self.attribute, + self.element.0.xml_path(), + self.target_version + ) + } +} + +#[pymethods] +impl IncompatibleAttributeValueError { + fn __repr__(&self) -> String { + format!("{:#?}", self) + } + + fn __str__(&self) -> String { + format!( + "Attribute value {} in attribue {} of element <{}> is incompatible with version {}", + self.attribute_value, + self.attribute, + self.element.0.xml_path(), + self.target_version + ) + } +} + +#[pymethods] +impl IncompatibleElementError { + fn __repr__(&self) -> String { + format!("{:#?}", self) + } + + fn __str__(&self) -> String { + format!( + "Element <{}> is incompatible with version {}", + self.element.0.xml_path(), + self.target_version + ) + } +} + +#[pymethods] +impl ElementType { + fn __repr__(&self) -> String { + format!("{:#?}", self.0) + } + + #[getter] + fn is_named(&self) -> bool { + self.0.is_named() + } + + #[getter] + fn is_ref(&self) -> bool { + self.0.is_ref() + } + + #[getter] + fn is_ordered(&self) -> bool { + self.0.is_ordered() + } + + #[getter] + fn splittable(&self) -> u32 { + self.0.splittable() + } + + fn splittable_in(&self, version: autosar_data_rs::AutosarVersion) -> bool { + self.0.splittable_in(version) + } + + fn reference_dest_value(&self, target: &ElementType) -> Option { + self.0.reference_dest_value(&target.0) + } + + fn find_sub_element( + &self, + target_name: autosar_data_rs::ElementName, + version: u32, + ) -> Option { + self.0 + .find_sub_element(target_name, version) + .map(|(etype, _)| ElementType(etype)) + } +} + +#[pymethods] +impl ContentType { + fn __repr__(&self) -> String { + format!("{:#?}", self) + } +} + +#[pymethods] +impl ElementsDfsIterator { + fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { + slf + } + + fn __next__(&mut self) -> Option { + Python::with_gil(|py| { + self.0.next().map(|(depth, elem)| { + PyTuple::new( + py, + [depth.to_object(py), Py::new(py, Element(elem)).unwrap().into_py(py)].iter(), + ) + .to_object(py) + }) + }) + } +} + +#[pymethods] +impl ArxmlFileElementsDfsIterator { + fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { + slf + } + + fn __next__(&mut self) -> Option { + Python::with_gil(|py| { + self.0.next().map(|(depth, elem)| { + PyTuple::new( + py, + [depth.to_object(py), Py::new(py, Element(elem)).unwrap().into_py(py)].iter(), + ) + .to_object(py) + }) + }) + } +} + +#[pymethods] +impl ElementContentIterator { + fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { + slf + } + + fn __next__(&mut self) -> Option { + let ec = self.0.next()?; + Python::with_gil(|py| match ec { + autosar_data_rs::ElementContent::Element(elem) => Some(Py::new(py, Element(elem)).unwrap().into_py(py)), + autosar_data_rs::ElementContent::CharacterData(cdata) => Some(character_data_to_object(&cdata)), + }) + } +} + +#[pymethods] +impl ElementsIterator { + fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { + slf + } + + fn __next__(&mut self) -> Option { + self.0.next().map(Element) + } +} + +#[pymethods] +impl AttributeIterator { + fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { + slf + } + + fn __next__(&mut self) -> Option { + let autosar_data_rs::Attribute { attrname, content } = self.0.next()?; + Some(Attribute { + attrname, + content: character_data_to_object(&content), + }) + } +} + +#[pymethods] +impl Attribute { + fn __repr__(&self) -> String { + format!( + "Attribute {{attrname={:?}, content=\"{}\" }}", + self.attrname, self.content + ) + } + + fn __str__(&self) -> String { + format!("Attribute({}=\"{}\")", self.attrname, self.content) + } + + #[getter] + fn attrname(&self) -> autosar_data_rs::AttributeName { + self.attrname + } + + #[getter] + fn content(&self) -> &PyObject { + &self.content + } +} + +/// A Python module implemented in Rust. +#[pymodule] +fn autosar_data(py: Python, m: &PyModule) -> PyResult<()> { + let submodule = PyModule::new(py, "specification")?; + submodule.add_class::()?; + submodule.add_class::()?; + submodule.add_class::()?; + submodule.add_class::()?; + submodule.add_class::()?; + m.add_submodule(submodule)?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add("AutosarDataError", py.get_type::())?; + Ok(()) +} + +fn extract_character_data(any: PyObject) -> PyResult { + Python::with_gil(|py| { + if let Ok(text) = any.extract::(py) { + Ok(autosar_data_rs::CharacterData::String(text)) + } else if let Ok(val) = any.extract::(py) { + Ok(autosar_data_rs::CharacterData::UnsignedInteger(val)) + } else if let Ok(val) = any.extract::(py) { + Ok(autosar_data_rs::CharacterData::Double(val)) + } else if let Ok(enumitem) = any.extract::(py) { + Ok(autosar_data_rs::CharacterData::Enum(enumitem)) + } else { + Err(AutosarDataError::new_err( + autosar_data_rs::AutosarDataError::IncorrectContentType.to_string(), + )) + } + }) +} + +fn character_data_to_object(cdata: &autosar_data_rs::CharacterData) -> PyObject { + Python::with_gil(|py| match cdata { + autosar_data_rs::CharacterData::Enum(enumitem) => Py::new(py, *enumitem).unwrap().into_py(py), + autosar_data_rs::CharacterData::String(s) => PyString::new(py, s).into_py(py), + autosar_data_rs::CharacterData::UnsignedInteger(val) => val.to_object(py), + autosar_data_rs::CharacterData::Double(val) => val.to_object(py), + }) +}