subversion-0.0.3/.cargo_vcs_info.json0000644000000001360000000000100132330ustar { "git": { "sha1": "58b172f06bae9413ce8121f154e57fb7266c1f8e" }, "path_in_vcs": "" }subversion-0.0.3/.github/workflows/rust.yml000064400000000000000000000006031046102023000171370ustar 00000000000000name: Rust on: push: pull_request: env: CARGO_TERM_COLOR: always jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Install apr run: sudo apt install -y libapr1-dev libaprutil1-dev libsvn-dev pkg-config libutf8proc-dev - name: Build run: cargo build --verbose - name: Run tests run: cargo test --verbose subversion-0.0.3/.gitignore000064400000000000000000000000131046102023000140050ustar 00000000000000/target *~ subversion-0.0.3/COPYING000064400000000000000000000261361046102023000130660ustar 00000000000000 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. subversion-0.0.3/Cargo.lock0000644000000507060000000000100112160ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "aho-corasick" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] [[package]] name = "apr" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8dc6d7c0cd828ef52a25eae13854be602013896e42475d07d0c96644288210ea" dependencies = [ "bindgen 0.69.1", "ctor", "pkg-config", "system-deps", ] [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "bindgen" version = "0.68.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "726e4313eb6ec35d2730258ad4e15b547ee75d6afaa1361a922e78e59b7d8078" dependencies = [ "bitflags 2.4.1", "cexpr", "clang-sys", "lazy_static", "lazycell", "log", "peeking_take_while", "prettyplease", "proc-macro2", "quote", "regex", "rustc-hash", "shlex", "syn", "which", ] [[package]] name = "bindgen" version = "0.69.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ffcebc3849946a7170a05992aac39da343a90676ab392c51a4280981d6379c2" dependencies = [ "bitflags 2.4.1", "cexpr", "clang-sys", "lazy_static", "lazycell", "log", "peeking_take_while", "prettyplease", "proc-macro2", "quote", "regex", "rustc-hash", "shlex", "syn", "which", ] [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[package]] name = "cexpr" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" dependencies = [ "nom", ] [[package]] name = "cfg-expr" version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03915af431787e6ffdcc74c645077518c6b6e01f80b761e0fbbfa288536311b3" dependencies = [ "smallvec", "target-lexicon", ] [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clang-sys" version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" dependencies = [ "glob", "libc", "libloading", ] [[package]] name = "ctor" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37e366bff8cd32dd8754b0991fb66b279dc48f598c3a18914852a6673deef583" dependencies = [ "quote", "syn", ] [[package]] name = "either" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" dependencies = [ "libc", "windows-sys", ] [[package]] name = "fastrand" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[package]] name = "form_urlencoded" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ "percent-encoding", ] [[package]] name = "glob" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "hashbrown" version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" [[package]] name = "heck" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "home" version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" dependencies = [ "windows-sys", ] [[package]] name = "idna" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ "unicode-bidi", "unicode-normalization", ] [[package]] name = "indexmap" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", "hashbrown", ] [[package]] name = "indoc" version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8" [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "lazycell" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "libloading" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" dependencies = [ "cfg-if", "winapi", ] [[package]] name = "linux-raw-sys" version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" [[package]] name = "lock_api" version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", ] [[package]] name = "log" version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "memchr" version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "memoffset" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" dependencies = [ "autocfg", ] [[package]] name = "minimal-lexical" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "nom" version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", "minimal-lexical", ] [[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.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", "windows-targets", ] [[package]] name = "peeking_take_while" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" [[package]] name = "percent-encoding" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pkg-config" version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "prettyplease" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" dependencies = [ "proc-macro2", "syn", ] [[package]] name = "proc-macro2" version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] [[package]] name = "pyo3" version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04e8453b658fe480c3e70c8ed4e3d3ec33eb74988bd186561b0cc66b85c3bc4b" dependencies = [ "cfg-if", "indoc", "libc", "memoffset", "parking_lot", "pyo3-build-config", "pyo3-ffi", "pyo3-macros", "unindent", ] [[package]] name = "pyo3-build-config" version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a96fe70b176a89cff78f2fa7b3c930081e163d5379b4dcdf993e3ae29ca662e5" dependencies = [ "once_cell", "target-lexicon", ] [[package]] name = "pyo3-ffi" version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "214929900fd25e6604661ed9cf349727c8920d47deff196c4e28165a6ef2a96b" dependencies = [ "libc", "pyo3-build-config", ] [[package]] name = "pyo3-macros" version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dac53072f717aa1bfa4db832b39de8c875b7c7af4f4a6fe93cdbf9264cf8383b" dependencies = [ "proc-macro2", "pyo3-macros-backend", "quote", "syn", ] [[package]] name = "pyo3-macros-backend" version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7774b5a8282bd4f25f803b1f0d945120be959a36c72e08e7cd031c792fdfd424" dependencies = [ "heck", "proc-macro2", "quote", "syn", ] [[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.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ "bitflags 1.3.2", ] [[package]] name = "regex" version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "rustc-hash" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" version = "0.38.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" dependencies = [ "bitflags 2.4.1", "errno", "libc", "linux-raw-sys", "windows-sys", ] [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" version = "1.0.190" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.190" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_spanned" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80" dependencies = [ "serde", ] [[package]] name = "shlex" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" [[package]] name = "smallvec" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" [[package]] name = "subversion" version = "0.0.3" dependencies = [ "apr", "bindgen 0.68.1", "ctor", "lazy_static", "pkg-config", "pyo3", "system-deps", "tempfile", "url", ] [[package]] name = "syn" version = "2.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "system-deps" version = "6.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a2d580ff6a20c55dfb86be5f9c238f67835d0e81cbdea8bf5680e0897320331" dependencies = [ "cfg-expr", "heck", "pkg-config", "toml", "version-compare", ] [[package]] name = "target-lexicon" version = "0.12.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c39fd04924ca3a864207c66fc2cd7d22d7c016007f9ce846cbb9326331930a" [[package]] name = "tempfile" version = "3.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" dependencies = [ "cfg-if", "fastrand", "redox_syscall", "rustix", "windows-sys", ] [[package]] name = "tinyvec" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ "tinyvec_macros", ] [[package]] name = "tinyvec_macros" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "toml" version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ff9e3abce27ee2c9a37f9ad37238c1bdd4e789c84ba37df76aa4d528f5072cc" dependencies = [ "serde", "serde_spanned", "toml_datetime", "toml_edit", ] [[package]] name = "toml_datetime" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" dependencies = [ "serde", ] [[package]] name = "toml_edit" version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", "winnow", ] [[package]] name = "unicode-bidi" version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" dependencies = [ "tinyvec", ] [[package]] name = "unindent" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce" [[package]] name = "url" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" dependencies = [ "form_urlencoded", "idna", "percent-encoding", ] [[package]] name = "version-compare" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" [[package]] name = "which" version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" dependencies = [ "either", "home", "once_cell", "rustix", ] [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ "windows-targets", ] [[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" [[package]] name = "winnow" version = "0.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "829846f3e3db426d4cee4510841b71a8e58aa2a76b1132579487ae430ccd9c7b" dependencies = [ "memchr", ] subversion-0.0.3/Cargo.toml0000644000000026030000000000100112320ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "subversion" version = "0.0.3" authors = ["Jelmer Vernooij "] description = "Rust bindings for Subversion" homepage = "https://github.com/jelmer/subversion-rs" readme = "README.md" license = "Apache-2.0" repository = "https://github.com/jelmer/subversion-rs.git" [package.metadata.system-deps] libsvn_client = "" libsvn_subr = "" libsvn_repos = "" libsvn_fs = "" libsvn_wc = "" libsvn_ra = "" [lib] doctest = false [dependencies.apr] version = "0.1" [dependencies.ctor] version = "0.2.5" [dependencies.lazy_static] version = "1.4.0" [dependencies.pyo3] version = ">=0.19" optional = true [dependencies.url] version = "2.2.0" optional = true [dev-dependencies.tempfile] version = "3.8.1" [build-dependencies.bindgen] version = "0.68" [build-dependencies.pkg-config] version = "0.3" [build-dependencies.system-deps] version = "6.2" [features] pyo3 = ["dep:pyo3"] url = ["dep:url"] subversion-0.0.3/Cargo.toml.orig000064400000000000000000000014611046102023000147140ustar 00000000000000[package] name = "subversion" version = "0.0.3" edition = "2021" authors = ["Jelmer Vernooij "] repository = "https://github.com/jelmer/subversion-rs.git" homepage = "https://github.com/jelmer/subversion-rs" license = "Apache-2.0" description = "Rust bindings for Subversion" [lib] doctest = false [dependencies] apr = { version = "0.1" } #apr = { path = "../apr-rs" } ctor = "0.2.5" lazy_static = "1.4.0" pyo3 = { version = ">=0.19", optional = true } url = { version = "2.2.0", optional = true } [features] pyo3 = ["dep:pyo3"] url = ["dep:url"] [build-dependencies] bindgen = "0.68" pkg-config = "0.3" system-deps = "6.2" [dev-dependencies] tempfile = "3.8.1" [package.metadata.system-deps] libsvn_client = "" libsvn_subr = "" libsvn_repos = "" libsvn_fs = "" libsvn_wc = "" libsvn_ra = "" subversion-0.0.3/README.md000064400000000000000000000010541046102023000133020ustar 00000000000000Subversion bindings for Rust ============================ This rust crate provides idiomatic bindings for the Subversion C libraries. At the moment, it only covers the "client" library but the aim is to support all of the public C API. Example: ```rust let mut ctx = subversion::client::Context::new().unwrap(); ctx.checkout( "http://svn.apache.org/repos/asf/subversion/trunk/subversion/libsvn_client", std::path::Path::new("libsvn_client"), Revision::Head, Revision::Head, Depth::Infinity, false, false, ) .unwrap(); ``` subversion-0.0.3/build.rs000064400000000000000000000035111046102023000134700ustar 00000000000000extern crate bindgen; extern crate pkg_config; fn create_svn_bindings(out_path: &std::path::Path) { let pc_svn = pkg_config::Config::new() .probe("libsvn_client") .unwrap_or_else(|e| panic!("Failed to find svn library: {}", e)); let svn_path = pc_svn .include_paths .iter() .find(|x| x.join("svn_client.h").exists()) .expect("Failed to find svn_client.h"); // Generate bindings using bindgen let svn_bindings = bindgen::Builder::default() .header(svn_path.join("svn_client.h").to_str().unwrap()) .header(svn_path.join("svn_dirent_uri.h").to_str().unwrap()) .header(svn_path.join("svn_version.h").to_str().unwrap()) .header(svn_path.join("svn_error.h").to_str().unwrap()) .header(svn_path.join("svn_opt.h").to_str().unwrap()) .header(svn_path.join("svn_repos.h").to_str().unwrap()) .header(svn_path.join("svn_time.h").to_str().unwrap()) .header(svn_path.join("svn_types.h").to_str().unwrap()) .header(svn_path.join("svn_types_impl.h").to_str().unwrap()) .header(svn_path.join("svn_wc.h").to_str().unwrap()) .header(svn_path.join("svn_props.h").to_str().unwrap()) .allowlist_file(".*/svn_.*.h") .blocklist_type("apr_.*") .derive_default(true) .clang_args( pc_svn .include_paths .iter() .map(|path| format!("-I{}", path.display())), ) .generate() .expect("Failed to generate bindings"); svn_bindings .write_to_file(out_path.join("subversion.rs")) .expect("Failed to write bindings"); } fn main() { system_deps::Config::new().probe().unwrap(); let out_path = std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap()); create_svn_bindings(out_path.as_path()); } subversion-0.0.3/disperse.conf000064400000000000000000000001171046102023000145070ustar 00000000000000# See https://github.com/jelmer/disperse timeout_days: 5 tag_name: "v$VERSION" subversion-0.0.3/examples/cat.rs000064400000000000000000000005451046102023000147620ustar 00000000000000 use subversion::{Revision}; fn main() { let mut ctx = subversion::client::Context::new().unwrap(); let mut stdout = std::io::stdout(); ctx.cat( "http://svn.apache.org/repos/asf/subversion/trunk/subversion/libsvn_client/cat.c", &mut stdout, Revision::Head, Revision::Head, true, ) .unwrap(); } subversion-0.0.3/examples/checkout.rs000064400000000000000000000006041046102023000160140ustar 00000000000000use subversion::{Depth, Revision}; fn main() { let mut ctx = subversion::client::Context::new().unwrap(); ctx.checkout( "http://svn.apache.org/repos/asf/subversion/trunk/subversion/libsvn_client", std::path::Path::new("libsvn_client"), Revision::Head, Revision::Head, Depth::Infinity, false, false, ) .unwrap(); } subversion-0.0.3/src/client.rs000064400000000000000000001201651046102023000144430ustar 00000000000000use crate::dirent::AsCanonicalDirent; use crate::generated::svn_error_t; use crate::generated::{ svn_client_add5, svn_client_checkout3, svn_client_cleanup2, svn_client_commit6, svn_client_conflict_get, svn_client_create_context2, svn_client_ctx_t, svn_client_delete4, svn_client_export5, svn_client_import5, svn_client_log5, svn_client_mkdir4, svn_client_proplist4, svn_client_relocate2, svn_client_status6, svn_client_switch3, svn_client_update4, svn_client_vacuum, svn_client_version, }; use crate::io::Dirent; use crate::uri::AsCanonicalUri; use crate::{Depth, Error, LogEntry, Revision, RevisionRange, Revnum, Version}; use apr::pool::PooledPtr; use apr::Pool; use std::collections::HashMap; pub fn version() -> Version { unsafe { Version(svn_client_version()) } } extern "C" fn wrap_commit_callback2( commit_info: *const crate::generated::svn_commit_info_t, baton: *mut std::ffi::c_void, pool: *mut apr::apr_pool_t, ) -> *mut crate::generated::svn_error_t { unsafe { let callback = baton as *mut &mut dyn FnMut(&crate::CommitInfo) -> Result<(), Error>; let mut callback = Box::from_raw(callback); match callback(&crate::CommitInfo(PooledPtr::in_pool( std::rc::Rc::new(Pool::from_raw(pool)), commit_info as *mut crate::generated::svn_commit_info_t, ))) { Ok(()) => std::ptr::null_mut(), Err(mut err) => err.as_mut_ptr(), } } } extern "C" fn wrap_filter_callback( baton: *mut std::ffi::c_void, filtered: *mut crate::generated::svn_boolean_t, local_abspath: *const i8, dirent: *const crate::generated::svn_io_dirent2_t, _pool: *mut apr::apr_pool_t, ) -> *mut svn_error_t { unsafe { let callback = baton as *mut &mut dyn FnMut(&std::path::Path, &Dirent) -> Result; let mut callback = Box::from_raw(callback); let local_abspath: &std::path::Path = std::ffi::CStr::from_ptr(local_abspath) .to_str() .unwrap() .as_ref(); let ret = callback(local_abspath, &Dirent::from(dirent)); if let Ok(ret) = ret { *filtered = ret as crate::generated::svn_boolean_t; std::ptr::null_mut() } else { ret.unwrap_err().as_mut_ptr() } } } extern "C" fn wrap_status_func( baton: *mut std::ffi::c_void, path: *const i8, status: *const crate::generated::svn_client_status_t, _pool: *mut apr::apr_pool_t, ) -> *mut crate::generated::svn_error_t { unsafe { let callback = baton as *mut &mut dyn FnMut(&std::path::Path, &Status) -> Result<(), Error>; let mut callback = Box::from_raw(callback); let path: &std::path::Path = std::ffi::CStr::from_ptr(path).to_str().unwrap().as_ref(); let ret = callback(path, &Status(status)); if let Err(mut err) = ret { err.as_mut_ptr() } else { std::ptr::null_mut() } } } extern "C" fn wrap_log_entry_receiver( baton: *mut std::ffi::c_void, log_entry: *mut crate::generated::svn_log_entry_t, pool: *mut apr::apr_pool_t, ) -> *mut crate::generated::svn_error_t { unsafe { let callback = baton as *mut &mut dyn FnMut(&LogEntry) -> Result<(), Error>; let mut callback = Box::from_raw(callback); let pool = apr::pool::Pool::from_raw(pool); let ret = callback(&LogEntry(apr::pool::PooledPtr::in_pool( std::rc::Rc::new(pool), log_entry, ))); if let Err(mut err) = ret { err.as_mut_ptr() } else { std::ptr::null_mut() } } } extern "C" fn wrap_proplist_receiver2( baton: *mut std::ffi::c_void, path: *const i8, props: *mut apr::hash::apr_hash_t, inherited_props: *mut apr::tables::apr_array_header_t, scratch_pool: *mut apr::apr_pool_t, ) -> *mut crate::generated::svn_error_t { unsafe { let scratch_pool = std::rc::Rc::new(apr::pool::Pool::from_raw(scratch_pool)); let callback = baton as *mut &mut dyn FnMut( &str, &HashMap>, Option<&[crate::InheritedItem]>, ) -> Result<(), Error>; let mut callback = Box::from_raw(callback); let path: &str = std::ffi::CStr::from_ptr(path).to_str().unwrap(); let mut props = apr::hash::Hash::<&str, *mut crate::generated::svn_string_t>::from_raw( PooledPtr::in_pool(scratch_pool.clone(), props), ); let props = props .iter() .map(|(k, v)| { ( String::from_utf8_lossy(k).to_string(), Vec::from_raw_parts((**v).data as *mut u8, (**v).len, (**v).len), ) }) .collect::>(); let inherited_props = if inherited_props.is_null() { None } else { let inherited_props = apr::tables::ArrayHeader::< *mut crate::generated::svn_prop_inherited_item_t, >::from_raw_parts(&scratch_pool, inherited_props); Some( inherited_props .iter() .map(|x| { crate::InheritedItem::from_raw(PooledPtr::in_pool(scratch_pool.clone(), x)) }) .collect::>(), ) }; let ret = callback(path, &props, inherited_props.as_deref()); if let Err(mut err) = ret { err.as_mut_ptr() } else { std::ptr::null_mut() } } } pub struct Info(*const crate::generated::svn_client_info2_t); impl Info { pub fn url(&self) -> &str { unsafe { let url = (*self.0).URL; std::ffi::CStr::from_ptr(url).to_str().unwrap() } } pub fn revision(&self) -> Revision { unsafe { Revision::from((*self.0).rev) } } pub fn kind(&self) -> crate::generated::svn_node_kind_t { unsafe { (*self.0).kind } } pub fn repos_root_url(&self) -> &str { unsafe { let url = (*self.0).repos_root_URL; std::ffi::CStr::from_ptr(url).to_str().unwrap() } } pub fn repos_uuid(&self) -> &str { unsafe { let uuid = (*self.0).repos_UUID; std::ffi::CStr::from_ptr(uuid).to_str().unwrap() } } pub fn last_changed_rev(&self) -> Revnum { unsafe { (*self.0).last_changed_rev } } pub fn last_changed_date(&self) -> apr::time::Time { unsafe { (*self.0).last_changed_date.into() } } pub fn last_changed_author(&self) -> &str { unsafe { let author = (*self.0).last_changed_author; std::ffi::CStr::from_ptr(author).to_str().unwrap() } } } extern "C" fn wrap_info_receiver2( baton: *mut std::ffi::c_void, abspath_or_url: *const i8, info: *const crate::generated::svn_client_info2_t, _scatch_pool: *mut apr::apr_pool_t, ) -> *mut crate::generated::svn_error_t { unsafe { let callback = baton as *mut &mut dyn FnMut(&std::path::Path, &Info) -> Result<(), Error>; let mut callback = Box::from_raw(callback); let abspath_or_url: &std::path::Path = std::ffi::CStr::from_ptr(abspath_or_url) .to_str() .unwrap() .as_ref(); let ret = callback(abspath_or_url, &Info(info)); if let Err(mut err) = ret { err.as_mut_ptr() } else { std::ptr::null_mut() } } } /// A client context. /// /// This is the main entry point for the client library. It holds client specific configuration and /// callbacks pub struct Context(apr::pool::PooledPtr); impl Context { pub fn new() -> Result { // call svn_client_create_context2 Ok(Context(apr::pool::PooledPtr::initialize(|pool| { let mut ctx = std::ptr::null_mut(); let ret = unsafe { svn_client_create_context2(&mut ctx, std::ptr::null_mut(), pool.as_mut_ptr()) }; Error::from_raw(ret)?; Ok::<_, Error>(ctx) })?)) } pub fn as_mut_ptr(&mut self) -> *mut svn_client_ctx_t { self.0.as_mut_ptr() } pub fn as_ptr(&self) -> *const svn_client_ctx_t { self.0.as_ptr() } /// Checkout a working copy from url to path. pub fn checkout<'a>( &mut self, url: impl AsCanonicalUri<'a>, path: impl AsCanonicalDirent<'a>, peg_revision: Revision, revision: Revision, depth: Depth, ignore_externals: bool, allow_unver_obstructions: bool, ) -> Result { // call svn_client_checkout2 let peg_revision = peg_revision.into(); let revision = revision.into(); let mut pool = Pool::default(); unsafe { let mut revnum = 0; let url = url.as_canonical_uri(&mut pool); let path = path.as_canonical_dirent(&mut pool); let err = svn_client_checkout3( &mut revnum, url.as_ptr(), path.as_ptr(), &peg_revision, &revision, depth.into(), ignore_externals.into(), allow_unver_obstructions.into(), &mut *self.0, pool.as_mut_ptr(), ); Error::from_raw(err)?; Ok(revnum) } } pub fn update( &mut self, paths: &[&str], revision: Revision, depth: Depth, depth_is_sticky: bool, ignore_externals: bool, allow_unver_obstructions: bool, adds_as_modifications: bool, make_parents: bool, ) -> Result, Error> { let mut pool = std::rc::Rc::new(Pool::default()); let mut result_revs = std::ptr::null_mut(); unsafe { let mut ps = apr::tables::ArrayHeader::in_pool(&pool, paths.len()); for path in paths { let path = std::ffi::CString::new(*path).unwrap(); ps.push(path.as_ptr() as *mut std::ffi::c_void); } let err = svn_client_update4( &mut result_revs, ps.as_ptr(), &revision.into(), depth.into(), depth_is_sticky.into(), ignore_externals.into(), allow_unver_obstructions.into(), adds_as_modifications.into(), make_parents.into(), &mut *self.0, std::rc::Rc::get_mut(&mut pool).unwrap().as_mut_ptr(), ); let result_revs: apr::tables::ArrayHeader = apr::tables::ArrayHeader::::from_raw_parts(&pool, result_revs); Error::from_raw(err)?; Ok(result_revs.iter().collect()) } } pub fn switch<'a>( &mut self, path: impl AsCanonicalDirent<'a>, url: impl AsCanonicalUri<'a>, peg_revision: Revision, revision: Revision, depth: Depth, depth_is_sticky: bool, ignore_externals: bool, allow_unver_obstructions: bool, make_parents: bool, ) -> Result { let mut pool = Pool::default(); let mut result_rev = 0; let path = path.as_canonical_dirent(&mut pool); let url = url.as_canonical_uri(&mut pool); unsafe { let err = svn_client_switch3( &mut result_rev, path.as_ptr(), url.as_ptr(), &peg_revision.into(), &revision.into(), depth.into(), depth_is_sticky.into(), ignore_externals.into(), allow_unver_obstructions.into(), make_parents.into(), &mut *self.0, pool.as_mut_ptr(), ); Error::from_raw(err)?; Ok(result_rev) } } pub fn add<'a>( &mut self, path: impl AsCanonicalDirent<'a>, depth: Depth, force: bool, no_ignore: bool, no_autoprops: bool, add_parents: bool, ) -> Result<(), Error> { let mut pool = Pool::default(); let path = path.as_canonical_dirent(&mut pool); unsafe { let err = svn_client_add5( path.as_ptr(), depth.into(), force.into(), no_ignore.into(), no_autoprops.into(), add_parents.into(), &mut *self.0, pool.as_mut_ptr(), ); Error::from_raw(err)?; Ok(()) } } pub fn mkdir( &mut self, paths: &[&str], make_parents: bool, revprop_table: std::collections::HashMap<&str, &[u8]>, commit_callback: &dyn FnMut(&crate::CommitInfo) -> Result<(), Error>, ) -> Result<(), Error> { let mut pool = std::rc::Rc::new(Pool::default()); unsafe { let mut rps = apr::hash::Hash::in_pool(&pool); for (k, v) in revprop_table { rps.set(k, &v); } let mut ps = apr::tables::ArrayHeader::in_pool(&pool, paths.len()); for path in paths { let path = std::ffi::CString::new(*path).unwrap(); ps.push(path.as_ptr() as *mut std::ffi::c_void); } let commit_callback = Box::into_raw(Box::new(commit_callback)); let err = svn_client_mkdir4( ps.as_ptr(), make_parents.into(), rps.as_ptr(), Some(wrap_commit_callback2), commit_callback as *mut std::ffi::c_void, &mut *self.0, std::rc::Rc::get_mut(&mut pool).unwrap().as_mut_ptr(), ); Error::from_raw(err)?; Ok(()) } } pub fn delete( &mut self, paths: &[&str], force: bool, keep_local: bool, revprop_table: std::collections::HashMap<&str, &str>, commit_callback: &dyn FnMut(&crate::CommitInfo) -> Result<(), Error>, ) -> Result<(), Error> { let mut pool = std::rc::Rc::new(Pool::default()); unsafe { let mut rps = apr::hash::Hash::in_pool(&pool); for (k, v) in revprop_table { rps.set(k, &v); } let mut ps = apr::tables::ArrayHeader::in_pool(&pool, paths.len()); for path in paths { let path = std::ffi::CString::new(*path).unwrap(); ps.push(path.as_ptr() as *mut std::ffi::c_void); } let commit_callback = Box::into_raw(Box::new(commit_callback)); let err = svn_client_delete4( ps.as_ptr(), force.into(), keep_local.into(), rps.as_ptr(), Some(wrap_commit_callback2), commit_callback as *mut std::ffi::c_void, &mut *self.0, std::rc::Rc::get_mut(&mut pool).unwrap().as_mut_ptr(), ); Error::from_raw(err)?; Ok(()) } } pub fn proplist( &mut self, target: &str, peg_revision: Revision, revision: Revision, depth: Depth, changelists: Option<&[&str]>, get_target_inherited_props: bool, receiver: &mut dyn FnMut( &str, &std::collections::HashMap>, Option<&[crate::InheritedItem]>, ) -> Result<(), Error>, ) -> Result<(), Error> { let mut pool = Pool::default(); let changelists = changelists.map(|cl| { cl.iter() .map(|cl| std::ffi::CString::new(*cl).unwrap()) .collect::>() }); let changelists = changelists.as_ref().map(|cl| { cl.iter() .map(|cl| cl.as_ptr() as *const i8) .collect::>() }); unsafe { let receiver = Box::into_raw(Box::new(receiver)); let err = svn_client_proplist4( target.as_ptr() as *const i8, &peg_revision.into(), &revision.into(), depth.into(), changelists .map(|cl| cl.as_ptr()) .unwrap_or(std::ptr::null()), get_target_inherited_props.into(), Some(wrap_proplist_receiver2), receiver as *mut std::ffi::c_void, &mut *self.0, pool.as_mut_ptr(), ); Error::from_raw(err)?; Ok(()) } } pub fn import<'a>( &mut self, path: impl AsCanonicalDirent<'a>, url: &str, depth: Depth, no_ignore: bool, no_autoprops: bool, ignore_unknown_node_types: bool, revprop_table: std::collections::HashMap<&str, &str>, filter_callback: &dyn FnMut(&mut bool, &std::path::Path, &Dirent) -> Result<(), Error>, commit_callback: &dyn FnMut(&crate::CommitInfo) -> Result<(), Error>, ) -> Result<(), Error> { let mut pool = std::rc::Rc::new(Pool::default()); let mut rps = apr::hash::Hash::in_pool(&pool); for (k, v) in revprop_table { rps.set(k, &v); } let path = path.as_canonical_dirent(std::rc::Rc::get_mut(&mut pool).unwrap()); unsafe { let filter_callback = Box::into_raw(Box::new(filter_callback)); let commit_callback = Box::into_raw(Box::new(commit_callback)); let err = svn_client_import5( path.as_ptr(), url.as_ptr() as *const i8, depth.into(), no_ignore.into(), no_autoprops.into(), ignore_unknown_node_types.into(), rps.as_ptr(), Some(wrap_filter_callback), filter_callback as *mut std::ffi::c_void, Some(wrap_commit_callback2), commit_callback as *mut std::ffi::c_void, &mut *self.0, std::rc::Rc::get_mut(&mut pool).unwrap().as_mut_ptr(), ); Error::from_raw(err)?; Ok(()) } } pub fn export<'a>( &mut self, from_path_or_url: &str, to_path: impl AsCanonicalDirent<'a>, peg_revision: Revision, revision: Revision, overwrite: bool, ignore_externals: bool, ignore_keywords: bool, depth: Depth, native_eol: crate::NativeEOL, ) -> Result { let mut pool = std::rc::Rc::new(Pool::default()); let native_eol: Option<&str> = native_eol.into(); let native_eol = native_eol.map(|s| std::ffi::CString::new(s).unwrap()); let mut revnum = 0; let to_path = to_path.as_canonical_dirent(std::rc::Rc::get_mut(&mut pool).unwrap()); unsafe { let err = svn_client_export5( &mut revnum, from_path_or_url.as_ptr() as *const i8, to_path.as_ptr(), &peg_revision.into(), &revision.into(), overwrite as i32, ignore_externals as i32, ignore_keywords as i32, depth as i32, native_eol.map(|s| s.as_ptr()).unwrap_or(std::ptr::null()), &mut *self.0, std::rc::Rc::get_mut(&mut pool).unwrap().as_mut_ptr(), ); Error::from_raw(err)?; Ok(revnum) } } pub fn commit( &mut self, targets: &[&str], depth: Depth, keep_locks: bool, keep_changelists: bool, commit_as_operations: bool, include_file_externals: bool, include_dir_externals: bool, changelists: Option<&[&str]>, revprop_table: std::collections::HashMap<&str, &str>, commit_callback: &dyn FnMut(&crate::CommitInfo) -> Result<(), Error>, ) -> Result<(), Error> { let mut pool = std::rc::Rc::new(Pool::default()); let mut rps = apr::hash::Hash::in_pool(&pool); for (k, v) in revprop_table { rps.set(k, &v); } unsafe { let mut ps = apr::tables::ArrayHeader::in_pool(&pool, targets.len()); for target in targets { let target = std::ffi::CString::new(*target).unwrap(); ps.push(target.as_ptr() as *mut std::ffi::c_void); } let mut cl = apr::tables::ArrayHeader::in_pool(&pool, 0); if let Some(changelists) = changelists { for changelist in changelists { let changelist = std::ffi::CString::new(*changelist).unwrap(); cl.push(changelist.as_ptr() as *mut std::ffi::c_void); } } let commit_callback = Box::into_raw(Box::new(commit_callback)); let err = svn_client_commit6( ps.as_ptr(), depth.into(), keep_locks.into(), keep_changelists.into(), commit_as_operations.into(), include_file_externals.into(), include_dir_externals.into(), cl.as_ptr(), rps.as_ptr(), Some(wrap_commit_callback2), commit_callback as *mut std::ffi::c_void, &mut *self.0, std::rc::Rc::get_mut(&mut pool).unwrap().as_mut_ptr(), ); Error::from_raw(err)?; Ok(()) } } pub fn status( &mut self, path: &str, revision: Revision, depth: Depth, get_all: bool, check_out_of_date: bool, check_working_copy: bool, no_ignore: bool, ignore_externals: bool, depth_as_sticky: bool, changelists: Option<&[&str]>, status_func: &dyn FnMut(&'_ str, &'_ Status) -> Result<(), Error>, ) -> Result { let mut pool = std::rc::Rc::new(Pool::default()); let mut cl = apr::tables::ArrayHeader::in_pool(&pool, 0); if let Some(changelists) = changelists { for changelist in changelists { let changelist = std::ffi::CString::new(*changelist).unwrap(); cl.push(changelist.as_ptr() as *mut std::ffi::c_void); } } unsafe { let status_func = Box::into_raw(Box::new(status_func)); let mut revnum = 0; let err = svn_client_status6( &mut revnum, &mut *self.0, path.as_ptr() as *const i8, &revision.into(), depth.into(), get_all.into(), check_out_of_date.into(), check_working_copy.into(), no_ignore.into(), ignore_externals.into(), depth_as_sticky.into(), cl.as_ptr(), Some(wrap_status_func), status_func as *mut std::ffi::c_void, std::rc::Rc::get_mut(&mut pool).unwrap().as_mut_ptr(), ); Error::from_raw(err)?; Ok(revnum as Revnum) } } pub fn log( &mut self, targets: &[&str], peg_revision: Revision, revision_ranges: &[RevisionRange], limit: i32, discover_changed_paths: bool, strict_node_history: bool, include_merged_revisions: bool, revprops: &[&str], log_entry_receiver: &dyn FnMut(&LogEntry) -> Result<(), Error>, ) -> Result<(), Error> { let mut pool = std::rc::Rc::new(Pool::default()); unsafe { let mut ps = apr::tables::ArrayHeader::in_pool(&pool, targets.len()); for target in targets { let target = std::ffi::CString::new(*target).unwrap(); ps.push(target.as_ptr() as *mut std::ffi::c_void); } let mut rrs = apr::tables::ArrayHeader::<*mut crate::generated::svn_opt_revision_range_t>::in_pool(&pool, revision_ranges.len()); for revision_range in revision_ranges { rrs.push(revision_range.to_c(std::rc::Rc::get_mut(&mut pool).unwrap())); } let mut rps = apr::tables::ArrayHeader::in_pool(&pool, revprops.len()); for revprop in revprops { let revprop = std::ffi::CString::new(*revprop).unwrap(); rps.push(revprop.as_ptr() as *mut std::ffi::c_void); } let log_entry_receiver = Box::into_raw(Box::new(log_entry_receiver)); let err = svn_client_log5( ps.as_ptr(), &peg_revision.into(), rrs.as_ptr(), limit, discover_changed_paths.into(), strict_node_history.into(), include_merged_revisions.into(), rps.as_ptr(), Some(wrap_log_entry_receiver), log_entry_receiver as *mut std::ffi::c_void, &mut *self.0, std::rc::Rc::get_mut(&mut pool).unwrap().as_mut_ptr(), ); Error::from_raw(err)?; Ok(()) } } pub fn args_to_target_array( &mut self, mut os: apr::getopt::Getopt, known_targets: &[&str], keep_last_origpath_on_truepath_collision: bool, ) -> Result, crate::Error> { let mut pool = apr::pool::Pool::new(); let known_targets = known_targets .iter() .map(|s| std::ffi::CString::new(*s).unwrap()) .collect::>(); let mut targets = std::ptr::null_mut(); let err = unsafe { crate::generated::svn_client_args_to_target_array2( &mut targets, os.as_mut_ptr(), known_targets .into_iter() .map(|s| s.as_ptr()) .collect::>() .as_ptr(), &mut *self.0, keep_last_origpath_on_truepath_collision.into(), pool.as_mut_ptr(), ) }; Error::from_raw(err)?; let targets = unsafe { apr::tables::ArrayHeader::<*const i8>::from_raw_parts(&std::rc::Rc::new(pool), targets) }; Ok(targets .iter() .map(|s| unsafe { std::ffi::CStr::from_ptr(*s as *const i8) }) .map(|s| s.to_str().unwrap().to_owned()) .collect::>()) } pub fn vacuum( &mut self, path: &str, remove_unversioned_items: bool, remove_ignored_items: bool, fix_recorded_timestamps: bool, vacuum_pristines: bool, include_externals: bool, ) -> Result<(), Error> { let mut pool = std::rc::Rc::new(Pool::default()); let path = std::ffi::CString::new(path).unwrap(); unsafe { let err = svn_client_vacuum( path.as_ptr() as *const i8, remove_unversioned_items.into(), remove_ignored_items.into(), fix_recorded_timestamps.into(), vacuum_pristines.into(), include_externals.into(), &mut *self.0, std::rc::Rc::get_mut(&mut pool).unwrap().as_mut_ptr(), ); Error::from_raw(err)?; Ok(()) } } pub fn cleanup( &mut self, path: &str, break_locks: bool, fix_recorded_timestamps: bool, clear_dav_cache: bool, vacuum_pristines: bool, include_externals: bool, ) -> Result<(), Error> { let mut pool = std::rc::Rc::new(Pool::default()); let path = std::ffi::CString::new(path).unwrap(); unsafe { let err = svn_client_cleanup2( path.as_ptr() as *const i8, break_locks.into(), fix_recorded_timestamps.into(), clear_dav_cache.into(), vacuum_pristines.into(), include_externals.into(), &mut *self.0, std::rc::Rc::get_mut(&mut pool).unwrap().as_mut_ptr(), ); Error::from_raw(err)?; Ok(()) } } pub fn relocate<'a>( &mut self, path: impl AsCanonicalDirent<'a>, from: &str, to: &str, ignore_externals: bool, ) -> Result<(), Error> { let mut pool = std::rc::Rc::new(Pool::default()); let from = std::ffi::CString::new(from).unwrap(); let to = std::ffi::CString::new(to).unwrap(); let path = path.as_canonical_dirent(std::rc::Rc::get_mut(&mut pool).unwrap()); unsafe { let err = svn_client_relocate2( path.as_ptr(), from.as_ptr() as *const i8, to.as_ptr() as *const i8, ignore_externals.into(), &mut *self.0, std::rc::Rc::get_mut(&mut pool).unwrap().as_mut_ptr(), ); Error::from_raw(err)?; Ok(()) } } pub fn conflict_get<'a>( &mut self, local_abspath: impl AsCanonicalDirent<'a>, ) -> Result { Ok(Conflict(apr::pool::PooledPtr::initialize(|pool| { let local_abspath = local_abspath.as_canonical_dirent(pool); let mut conflict: *mut crate::generated::svn_client_conflict_t = std::ptr::null_mut(); unsafe { let err = svn_client_conflict_get( &mut conflict, local_abspath.as_ptr(), &mut *self.0, pool.as_mut_ptr(), Pool::new().as_mut_ptr(), ); Error::from_raw(err)?; Ok::<_, Error>(conflict) } })?)) } pub fn cat( &mut self, path_or_url: &str, stream: &mut dyn std::io::Write, peg_revision: Revision, revision: Revision, expand_keywords: bool, ) -> Result>, Error> { let mut pool = std::rc::Rc::new(Pool::default()); let path_or_url = std::ffi::CString::new(path_or_url).unwrap(); let mut s = crate::io::wrap_write(stream)?; let mut props: *mut apr::hash::apr_hash_t = std::ptr::null_mut(); unsafe { let err = crate::generated::svn_client_cat3( &mut props, s.as_mut_ptr(), path_or_url.as_ptr(), &peg_revision.into(), &revision.into(), expand_keywords.into(), self.as_mut_ptr(), std::rc::Rc::get_mut(&mut pool).unwrap().as_mut_ptr(), apr::pool::Pool::new().as_mut_ptr(), ); Error::from_raw(err)?; let mut props = apr::hash::Hash::<&str, &[u8]>::from_raw(PooledPtr::in_pool(pool, props)); Ok(props .iter() .map(|(k, v)| (String::from_utf8_lossy(k).to_string(), v.to_vec())) .collect()) } } pub fn lock(&mut self, targets: &[&str], comment: &str, steal_lock: bool) -> Result<(), Error> { let mut pool = std::rc::Rc::new(Pool::default()); let targets = targets .iter() .map(|s| std::ffi::CString::new(*s).unwrap()) .collect::>(); let targets = targets .iter() .map(|s| s.as_ptr()) .collect::>(); let comment = std::ffi::CString::new(comment).unwrap(); unsafe { let err = crate::generated::svn_client_lock( targets.as_ptr(), comment.as_ptr(), steal_lock.into(), self.as_mut_ptr(), std::rc::Rc::get_mut(&mut pool).unwrap().as_mut_ptr(), ); Error::from_raw(err)?; Ok(()) } } pub fn unlock(&mut self, targets: &[&str], break_lock: bool) -> Result<(), Error> { let mut pool = std::rc::Rc::new(Pool::default()); let targets = targets .iter() .map(|s| std::ffi::CString::new(*s).unwrap()) .collect::>(); let targets = targets .iter() .map(|s| s.as_ptr()) .collect::>(); unsafe { let err = crate::generated::svn_client_unlock( targets.as_ptr(), break_lock.into(), self.as_mut_ptr(), std::rc::Rc::get_mut(&mut pool).unwrap().as_mut_ptr(), ); Error::from_raw(err)?; Ok(()) } } pub fn get_wc_root<'a>( &mut self, path: impl AsCanonicalDirent<'a>, ) -> Result { let mut pool = std::rc::Rc::new(Pool::default()); let path = path.as_canonical_dirent(std::rc::Rc::get_mut(&mut pool).unwrap()); let mut wc_root: *const i8 = std::ptr::null(); unsafe { let err = crate::generated::svn_client_get_wc_root( &mut wc_root, path.as_ptr(), self.as_mut_ptr(), std::rc::Rc::get_mut(&mut pool).unwrap().as_mut_ptr(), apr::pool::Pool::new().as_mut_ptr(), ); Error::from_raw(err)?; Ok(std::ffi::CStr::from_ptr(wc_root).to_str().unwrap().into()) } } pub fn min_max_revisions<'a>( &mut self, local_abspath: impl AsCanonicalDirent<'a>, committed: bool, ) -> Result<(Revnum, Revnum), Error> { let mut scratch_pool = apr::pool::Pool::new(); let local_abspath = local_abspath.as_canonical_dirent(&mut scratch_pool); let mut min_revision: crate::generated::svn_revnum_t = 0; let mut max_revision: crate::generated::svn_revnum_t = 0; unsafe { let err = crate::generated::svn_client_min_max_revisions( &mut min_revision, &mut max_revision, local_abspath.as_ptr(), committed as i32, self.as_mut_ptr(), scratch_pool.as_mut_ptr(), ); Error::from_raw(err)?; Ok((min_revision, max_revision)) } } pub fn url_from_path<'a>(&mut self, path: impl AsCanonicalUri<'a>) -> Result { let mut pool = Pool::default(); let path = path.as_canonical_uri(&mut pool); let mut url: *const i8 = std::ptr::null(); unsafe { let err = crate::generated::svn_client_url_from_path2( &mut url, path.as_ptr(), self.as_mut_ptr(), pool.as_mut_ptr(), pool.as_mut_ptr(), ); Error::from_raw(err)?; Ok(std::ffi::CStr::from_ptr(url).to_str().unwrap().into()) } } pub fn get_repos_root(&mut self, path_or_url: &str) -> Result<(String, String), Error> { let mut pool = Pool::default(); let path_or_url = std::ffi::CString::new(path_or_url).unwrap(); let mut repos_root: *const i8 = std::ptr::null(); let mut repos_uuid: *const i8 = std::ptr::null(); unsafe { let err = crate::generated::svn_client_get_repos_root( &mut repos_root, &mut repos_uuid, path_or_url.as_ptr() as *const i8, self.as_mut_ptr(), pool.as_mut_ptr(), pool.as_mut_ptr(), ); Error::from_raw(err)?; Ok(( std::ffi::CStr::from_ptr(repos_root) .to_str() .unwrap() .into(), std::ffi::CStr::from_ptr(repos_uuid) .to_str() .unwrap() .into(), )) } } pub fn open_raw_session( &mut self, url: &str, wri_path: &std::path::Path, ) -> Result { let url = std::ffi::CString::new(url).unwrap(); let wri_path = std::ffi::CString::new(wri_path.to_str().unwrap()).unwrap(); let session = PooledPtr::initialize(|pool| unsafe { let mut scratch_pool = Pool::default(); let mut session: *mut crate::generated::svn_ra_session_t = std::ptr::null_mut(); let err = crate::generated::svn_client_open_ra_session2( &mut session, url.as_ptr() as *const i8, wri_path.as_ptr() as *const i8, self.as_mut_ptr(), pool.as_mut_ptr(), scratch_pool.as_mut_ptr(), ); Error::from_raw(err)?; Ok::<_, Error>(session) })?; Ok(crate::ra::Session::from_raw(session)) } pub fn info( &mut self, abspath_or_url: &str, peg_revision: Revision, revision: Revision, depth: Depth, fetch_excluded: bool, fetch_actual_only: bool, include_externals: bool, changelists: Option<&[&str]>, receiver: &dyn FnMut(&Info) -> Result<(), Error>, ) -> Result<(), Error> { let mut pool = Pool::default(); let abspath_or_url = std::ffi::CString::new(abspath_or_url).unwrap(); let changelists = changelists.map(|cl| { cl.iter() .map(|cl| std::ffi::CString::new(*cl).unwrap()) .collect::>() }); let changelists = changelists.as_ref().map(|cl| { cl.iter() .map(|cl| cl.as_ptr() as *const i8) .collect::>() }); let mut receiver = receiver; let receiver = &mut receiver as *mut _ as *mut std::ffi::c_void; unsafe { let err = crate::generated::svn_client_info4( abspath_or_url.as_ptr() as *const i8, &peg_revision.into(), &revision.into(), depth.into(), fetch_excluded as i32, fetch_actual_only as i32, include_externals as i32, changelists.map_or(std::ptr::null(), |cl| cl.as_ptr()), Some(wrap_info_receiver2), receiver, self.as_mut_ptr(), pool.as_mut_ptr(), ); Error::from_raw(err)?; Ok(()) } } } impl Default for Context { fn default() -> Self { Self::new().unwrap() } } #[cfg(test)] mod tests { use super::*; #[test] fn test_version() { let version = version(); assert_eq!(version.major(), 1); } #[test] fn test_open() { let td = tempfile::tempdir().unwrap(); let repo_path = td.path().join("repo"); let mut repos = crate::repos::Repos::create(&repo_path).unwrap(); assert_eq!(repos.path(), td.path().join("repo")); let mut ctx = Context::new().unwrap(); let dirent = crate::dirent::Dirent::from(repo_path.to_str().unwrap()); let url = dirent.canonicalize().to_file_url().unwrap(); let revnum = ctx .checkout( url, &td.path().join("wc"), Revision::Head, Revision::Head, Depth::Infinity, false, false, ) .unwrap(); assert_eq!(revnum, 0); } } pub struct Status(pub(crate) *const crate::generated::svn_client_status_t); pub struct Conflict(pub(crate) apr::pool::PooledPtr); impl Conflict { pub fn prop_get_description(&mut self) -> Result { let mut pool = apr::pool::Pool::new(); let mut description: *const i8 = std::ptr::null_mut(); let err = unsafe { crate::generated::svn_client_conflict_prop_get_description( &mut description, self.0.as_mut_ptr(), pool.as_mut_ptr(), pool.as_mut_ptr(), ) }; Error::from_raw(err)?; Ok(unsafe { std::ffi::CStr::from_ptr(description) } .to_str() .unwrap() .to_owned()) } } subversion-0.0.3/src/config.rs000064400000000000000000000001431046102023000144230ustar 00000000000000pub struct Config<'pool>(apr::hash::Hash<'pool, &'pool str, *mut crate::generated::svn_config_t>); subversion-0.0.3/src/dirent.rs000064400000000000000000000250261046102023000144520ustar 00000000000000use crate::uri::Uri; use crate::Canonical; use crate::Error; use apr::pool::Pooled; pub struct Dirent<'a>(*const i8, std::marker::PhantomData<&'a ()>); impl ToString for Dirent<'_> { fn to_string(&self) -> String { self.as_str().to_string() } } impl std::fmt::Debug for Dirent<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_tuple("Dirent").field(&self.to_string()).finish() } } impl std::cmp::PartialEq for Dirent<'_> { fn eq(&self, other: &Self) -> bool { self.as_str() == other.as_str() } } impl std::cmp::Eq for Dirent<'_> {} impl<'a> Dirent<'a> { pub fn pooled(s: &str) -> Pooled { Pooled::initialize(|pool| { Ok::<_, crate::Error>(Self( apr::strings::pstrdup(s, pool), std::marker::PhantomData, )) }) .unwrap() } pub fn from_raw(raw: *const i8) -> Self { Self(raw, std::marker::PhantomData) } pub fn from_cstr(cstr: &std::ffi::CStr) -> Self { Self(cstr.as_ptr(), std::marker::PhantomData) } pub fn as_str(&self) -> &str { unsafe { std::ffi::CStr::from_ptr(self.0) } .to_str() .expect("invalid utf8") } pub fn canonicalize(&'_ self) -> Pooled> { Pooled::initialize(|pool| unsafe { Ok::<_, crate::Error>(Canonical(Self( crate::generated::svn_dirent_canonicalize(self.as_ptr(), pool.as_mut_ptr()), std::marker::PhantomData, ))) }) .unwrap() } pub fn canonicalize_in<'b>(&'_ self, pool: &'b mut apr::pool::Pool) -> Canonical where 'a: 'b, { unsafe { let canonical = crate::generated::svn_dirent_canonicalize(self.as_ptr(), pool.as_mut_ptr()); Canonical(Self(canonical, std::marker::PhantomData)) } } pub fn canonicalize_safe( &self, ) -> Result<(Pooled>, Pooled), crate::Error> { let mut pool = apr::pool::Pool::new(); unsafe { let mut canonical = std::ptr::null(); let mut non_canonical = std::ptr::null(); let err = crate::generated::svn_dirent_canonicalize_safe( &mut canonical, &mut non_canonical, self.as_ptr(), pool.as_mut_ptr(), apr::pool::Pool::new().as_mut_ptr(), ); let pool = std::rc::Rc::new(pool); Error::from_raw(err)?; Ok(( Pooled::in_pool( pool.clone(), Canonical(Self(canonical, std::marker::PhantomData)), ), Pooled::in_pool(pool, Self(non_canonical, std::marker::PhantomData)), )) } } pub fn as_ptr(&self) -> *const i8 { self.0 } pub fn is_canonical(&self) -> bool { unsafe { crate::generated::svn_dirent_is_canonical( self.as_ptr(), apr::pool::Pool::new().as_mut_ptr(), ) != 0 } } pub fn check_canonical(self) -> Option> { if self.is_canonical() { Some(Canonical(self)) } else { None } } pub fn is_root(&self, length: usize) -> bool { unsafe { crate::generated::svn_dirent_is_root(self.as_ptr(), length) != 0 } } pub fn is_absolute(&self) -> bool { unsafe { crate::generated::svn_dirent_is_absolute(self.as_ptr()) != 0 } } pub fn dirname(&self) -> Pooled { Pooled::initialize(|pool| unsafe { Ok::<_, crate::Error>(Self( crate::generated::svn_dirent_dirname(self.as_ptr(), pool.as_mut_ptr()) as *const i8, std::marker::PhantomData, )) }) .unwrap() } pub fn basename(&self) -> Pooled { Pooled::initialize(|pool| unsafe { Ok::<_, crate::Error>(Self( crate::generated::svn_dirent_basename(self.as_ptr(), pool.as_mut_ptr()), std::marker::PhantomData, )) }) .unwrap() } pub fn split(&self) -> (Pooled, Pooled) { let mut pool = apr::pool::Pool::new(); unsafe { let mut dirname = std::ptr::null(); let mut basename = std::ptr::null(); crate::generated::svn_dirent_split( &mut dirname, &mut basename, self.as_ptr(), pool.as_mut_ptr(), ); let pool = std::rc::Rc::new(pool); ( Pooled::in_pool(pool.clone(), Self(dirname, std::marker::PhantomData)), Pooled::in_pool(pool, Self(basename, std::marker::PhantomData)), ) } } pub fn is_ancestor(&self, other: &Self) -> bool { unsafe { crate::generated::svn_dirent_is_ancestor(self.as_ptr(), other.as_ptr()) != 0 } } pub fn skip_ancestor(&self, child: &Pooled) -> Option> { unsafe { let result = crate::generated::svn_dirent_skip_ancestor(self.as_ptr(), child.as_ptr()); if result.is_null() { Some(Pooled::in_pool( child.pool(), Self(result, std::marker::PhantomData), )) } else { None } } } pub fn is_under_root( &self, root: &'_ Canonical, ) -> Result>>, crate::Error> { let mut under_root = 0; let mut result_path = std::ptr::null(); unsafe { let result_path = Pooled::initialize(|pool| { let err = crate::generated::svn_dirent_is_under_root( &mut under_root, &mut result_path, root.as_ptr(), self.as_ptr(), pool.as_mut_ptr(), ); Error::from_raw(err)?; Ok::<_, Error>(Canonical(Self(result_path, std::marker::PhantomData))) })?; if under_root == 0 { return Ok(None); } assert!(!result_path.0 .0.is_null()); Ok(Some(result_path)) } } pub fn absolute(&self) -> Result, crate::Error> { Pooled::initialize(|pool| unsafe { let mut result = std::ptr::null(); let err = crate::generated::svn_dirent_get_absolute( &mut result, self.as_ptr(), pool.as_mut_ptr(), ); Error::from_raw(err)?; Ok::<_, Error>(Self(result, std::marker::PhantomData)) }) } } impl<'a> Canonical> { pub fn to_file_url<'b>(&self) -> Result>, crate::Error> { assert!(self.is_canonical()); Pooled::initialize(|pool| unsafe { let mut url = std::ptr::null(); let err = crate::generated::svn_uri_get_file_url_from_dirent( &mut url, self.as_ptr(), pool.as_mut_ptr(), ); Error::from_raw(err)?; Ok::<_, Error>(Uri::from_raw(url)) }) } } impl<'a, 'b> TryFrom>> for Pooled> { type Error = crate::Error; fn try_from(dirent: Canonical>) -> Result { dirent.to_file_url() } } impl From> for String { fn from(dirent: Dirent) -> Self { let c = unsafe { std::ffi::CStr::from_ptr(dirent.as_ptr()) }; c.to_string_lossy().into_owned() } } impl From> for &str { fn from(dirent: Dirent) -> Self { let c = unsafe { std::ffi::CStr::from_ptr(dirent.as_ptr()) }; c.to_str().unwrap() } } impl<'a> From<&'a str> for Dirent<'a> { fn from(s: &'a str) -> Self { Self(s.as_ptr() as *const i8, std::marker::PhantomData) } } impl<'a> From<&'a std::path::Path> for Dirent<'a> { fn from(path: &'a std::path::Path) -> Self { Self::from(path.to_str().unwrap()) } } impl<'a> From> for std::path::PathBuf { fn from(dirent: Dirent<'a>) -> Self { let c = unsafe { std::ffi::CStr::from_ptr(dirent.as_ptr()) }; std::path::PathBuf::from(c.to_string_lossy().into_owned()) } } #[cfg(test)] mod tests { #[test] fn test_as_str() { let dirent = super::Dirent::pooled("/foo/bar"); assert_eq!("/foo/bar", dirent.as_str()); } #[test] fn test_canonicalize() { let dirent = super::Dirent::pooled("/foo/bar"); assert!(dirent.is_canonical()); assert_eq!(*super::Dirent::pooled("/foo/bar"), *dirent); assert_eq!(*super::Dirent::pooled("/foo/bar"), **dirent.canonicalize()); } #[test] fn test_to_uri() { let dirent = super::Dirent::pooled("/foo/bar"); assert_eq!( *super::Uri::pooled("file:///foo/bar"), *dirent.canonicalize().to_file_url().unwrap() ); } #[test] fn test_is_under_root() { let root = super::Dirent::pooled("/foo").canonicalize(); let child = super::Dirent::pooled("bar"); let result_path = child.is_under_root(&root).unwrap(); assert!(result_path.is_some()); assert_eq!(*super::Dirent::pooled("/foo/bar"), **result_path.unwrap()); } } pub trait AsCanonicalDirent<'a> { fn as_canonical_dirent(self, scratch_pool: &mut apr::pool::Pool) -> Canonical>; } impl<'a> AsCanonicalDirent<'a> for &str { fn as_canonical_dirent(self, scratch_pool: &mut apr::pool::Pool) -> Canonical> { Dirent::pooled(self).canonicalize_in(scratch_pool) } } impl<'a> AsCanonicalDirent<'a> for &std::path::PathBuf { fn as_canonical_dirent(self, scratch_pool: &mut apr::pool::Pool) -> Canonical> { Dirent::pooled(self.to_str().unwrap()).canonicalize_in(scratch_pool) } } impl<'a> AsCanonicalDirent<'a> for Dirent<'a> { fn as_canonical_dirent(self, scratch_pool: &mut apr::pool::Pool) -> Canonical> { self.canonicalize_in(scratch_pool) } } impl<'a> AsCanonicalDirent<'a> for Canonical> { fn as_canonical_dirent(self, _scratch_pool: &mut apr::pool::Pool) -> Canonical> { self } } impl<'a> AsCanonicalDirent<'a> for &'a std::path::Path { fn as_canonical_dirent(self, scratch_pool: &mut apr::pool::Pool) -> Canonical> { Dirent::pooled(self.to_str().unwrap()).canonicalize_in(scratch_pool) } } subversion-0.0.3/src/error.rs000064400000000000000000000072421046102023000143160ustar 00000000000000use crate::generated; use generated::svn_error_t; // Errors are a bit special; they own their own pool, so don't need to use PooledPtr pub struct Error(*mut svn_error_t); impl Error { pub fn new(status: apr::Status, child: Option, msg: &str) -> Self { let msg = std::ffi::CString::new(msg).unwrap(); let child = child.map(|e| e.0).unwrap_or(std::ptr::null_mut()); let err = unsafe { generated::svn_error_create(status as i32, child, msg.as_ptr()) }; Self(err) } pub fn apr_err(&self) -> apr::Status { unsafe { (*self.0).apr_err }.into() } pub fn as_mut_ptr(&mut self) -> *mut svn_error_t { self.0 } pub fn as_ptr(&self) -> *const svn_error_t { self.0 } pub fn from_raw(err: *mut svn_error_t) -> Result<(), Self> { if err.is_null() { Ok(()) } else { Err(Self(err)) } } pub fn line(&self) -> i64 { unsafe { (*self.0).line } } pub fn file(&self) -> Option<&str> { unsafe { let file = (*self.0).file; if file.is_null() { None } else { Some(std::ffi::CStr::from_ptr(file).to_str().unwrap()) } } } pub fn child(&self) -> Option { unsafe { let child = (*self.0).child; if child.is_null() { None } else { Some(Error(child)) } } } pub fn message(&self) -> &str { unsafe { let message = (*self.0).message; std::ffi::CStr::from_ptr(message).to_str().unwrap() } } pub fn find_cause(&self, status: apr::Status) -> Option { unsafe { let err = generated::svn_error_find_cause(self.0, status as i32); if err.is_null() { None } else { Some(Error(err)) } } } pub fn purge_tracing(&self) -> Self { unsafe { Self(generated::svn_error_purge_tracing(self.0)) } } pub unsafe fn detach(&mut self) -> *mut svn_error_t { let err = self.0; self.0 = std::ptr::null_mut(); err } } impl Clone for Error { fn clone(&self) -> Self { unsafe { Self(generated::svn_error_dup(self.0)) } } } impl Drop for Error { fn drop(&mut self) { unsafe { generated::svn_error_clear(self.0) } } } impl std::fmt::Debug for Error { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { writeln!( f, "{}:{}: {}", self.file().unwrap_or(""), self.line(), self.message() )?; let mut n = self.child(); while let Some(err) = n { writeln!( f, "{}:{}: {}", err.file().unwrap_or(""), err.line(), err.message() )?; n = err.child(); } Ok(()) } } impl std::fmt::Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { writeln!(f, "{}", self.message())?; Ok(()) } } impl std::error::Error for Error {} impl From for Error { fn from(err: std::io::Error) -> Self { Self::new(apr::Status::from(err.kind()), None, &err.to_string()) } } impl From for std::io::Error { fn from(err: Error) -> Self { let errno = err.apr_err().raw_os_error(); errno.map_or( std::io::Error::new(std::io::ErrorKind::Other, err.message()), std::io::Error::from_raw_os_error, ) } } subversion-0.0.3/src/fs.rs000064400000000000000000000101771046102023000135760ustar 00000000000000use crate::Error; use apr::pool::PooledPtr; pub struct Fs(pub(crate) PooledPtr); impl Fs { pub fn create(path: &std::path::Path) -> Result { unsafe { Ok(Self(PooledPtr::initialize(|pool| { let mut fs_ptr = std::ptr::null_mut(); let path = std::ffi::CString::new(path.to_str().unwrap()).unwrap(); let err = crate::generated::svn_fs_create2( &mut fs_ptr, path.as_ptr(), std::ptr::null_mut(), pool.as_mut_ptr(), apr::pool::Pool::new().as_mut_ptr(), ); Error::from_raw(err)?; Ok::<_, Error>(fs_ptr) })?)) } } pub fn open(path: &std::path::Path) -> Result { unsafe { Ok(Self(PooledPtr::initialize(|pool| { let mut fs_ptr = std::ptr::null_mut(); let path = std::ffi::CString::new(path.to_str().unwrap()).unwrap(); let err = crate::generated::svn_fs_open2( &mut fs_ptr, path.as_ptr(), std::ptr::null_mut(), pool.as_mut_ptr(), apr::pool::Pool::new().as_mut_ptr(), ); Error::from_raw(err)?; Ok::<_, Error>(fs_ptr) })?)) } } pub fn path(&mut self) -> std::path::PathBuf { unsafe { let mut pool = apr::pool::Pool::new(); let path = crate::generated::svn_fs_path(self.0.as_mut_ptr(), pool.as_mut_ptr()); std::ffi::CStr::from_ptr(path) .to_string_lossy() .into_owned() .into() } } pub fn get_uuid(&mut self) -> Result { unsafe { let mut pool = apr::pool::Pool::new(); let mut uuid = std::ptr::null(); let err = crate::generated::svn_fs_get_uuid( self.0.as_mut_ptr(), &mut uuid, pool.as_mut_ptr(), ); Error::from_raw(err)?; Ok(std::ffi::CStr::from_ptr(uuid) .to_string_lossy() .into_owned()) } } pub fn set_uuid(&mut self, uuid: &str) -> Result<(), crate::Error> { unsafe { let uuid = std::ffi::CString::new(uuid).unwrap(); let err = crate::generated::svn_fs_set_uuid( self.0.as_mut_ptr(), uuid.as_ptr(), apr::pool::Pool::new().as_mut_ptr(), ); Error::from_raw(err)?; Ok(()) } } pub fn unlock( &mut self, path: &std::path::Path, token: &str, break_lock: bool, ) -> Result<(), crate::Error> { unsafe { let path = std::ffi::CString::new(path.to_str().unwrap()).unwrap(); let token = std::ffi::CString::new(token).unwrap(); let err = crate::generated::svn_fs_unlock( self.0.as_mut_ptr(), path.as_ptr(), token.as_ptr(), break_lock as i32, apr::pool::Pool::new().as_mut_ptr(), ); Error::from_raw(err)?; Ok(()) } } } pub fn fs_type(path: &std::path::Path) -> Result { let path = std::ffi::CString::new(path.to_str().unwrap()).unwrap(); unsafe { let mut pool = apr::pool::Pool::new(); let mut fs_type = std::ptr::null(); let err = crate::generated::svn_fs_type(&mut fs_type, path.as_ptr(), pool.as_mut_ptr()); Error::from_raw(err)?; Ok(std::ffi::CStr::from_ptr(fs_type) .to_string_lossy() .into_owned()) } } pub fn delete_fs(path: &std::path::Path) -> Result<(), crate::Error> { let path = std::ffi::CString::new(path.to_str().unwrap()).unwrap(); unsafe { let err = crate::generated::svn_fs_delete_fs(path.as_ptr(), apr::pool::Pool::new().as_mut_ptr()); Error::from_raw(err)?; Ok(()) } } subversion-0.0.3/src/generated.rs000064400000000000000000000013331046102023000151160ustar 00000000000000#![allow(bad_style)] #![allow(non_snake_case)] #![allow(non_upper_case_globals)] #![allow(non_camel_case_types)] #![allow(dead_code)] #![allow(clippy::upper_case_acronyms)] include!(concat!(env!("OUT_DIR"), "/subversion.rs")); use apr::apr_byte_t; use apr::apr_file_t; use apr::apr_finfo_t; use apr::apr_getopt_t; use apr::apr_int64_t; use apr::apr_off_t; use apr::apr_pool_t; use apr::apr_size_t; use apr::apr_status_t; use apr::apr_time_t; use apr::apr_int32_t; use apr::apr_uint32_t; use apr::apr_fileperms_t; use apr::apr_proc_t; use apr::apr_uint64_t; use apr::apr_dir_t; use apr::hash::apr_hash_t; use apr::tables::apr_array_header_t; use apr::apr_getopt_option_t; use apr::apr_exit_why_e; use apr::apr_seek_where_t; subversion-0.0.3/src/io.rs000064400000000000000000000210561046102023000135730ustar 00000000000000use crate::generated::svn_io_dirent2_t; use crate::Error; pub struct Dirent(*const svn_io_dirent2_t); impl From<*const svn_io_dirent2_t> for Dirent { fn from(ptr: *const svn_io_dirent2_t) -> Self { Self(ptr) } } pub struct Stream(apr::pool::PooledPtr); impl Stream { pub fn empty() -> Self { Self( apr::pool::PooledPtr::initialize(|pool| { let stream = unsafe { crate::generated::svn_stream_empty(pool.as_mut_ptr()) }; Ok::<_, crate::Error>(stream) }) .unwrap(), ) } pub fn as_mut_ptr(&mut self) -> *mut crate::generated::svn_stream_t { self.0.as_mut_ptr() } pub fn as_ptr(&self) -> *const crate::generated::svn_stream_t { self.0.as_ptr() } pub fn open_readonly(path: &std::path::Path) -> Result { Ok(Self(apr::pool::PooledPtr::initialize(|pool| { let mut stream = std::ptr::null_mut(); let path = std::ffi::CString::new(path.to_str().unwrap()).unwrap(); let err = unsafe { crate::generated::svn_stream_open_readonly( &mut stream, path.as_ptr(), pool.as_mut_ptr(), apr::pool::Pool::new().as_mut_ptr(), ) }; Error::from_raw(err)?; Ok::<_, Error>(stream) })?)) } pub fn open_writable(path: &std::path::Path) -> Result { Ok(Self(apr::pool::PooledPtr::initialize(|pool| { let mut stream = std::ptr::null_mut(); let path = std::ffi::CString::new(path.to_str().unwrap()).unwrap(); let err = unsafe { crate::generated::svn_stream_open_writable( &mut stream, path.as_ptr(), pool.as_mut_ptr(), apr::pool::Pool::new().as_mut_ptr(), ) }; Error::from_raw(err)?; Ok::<_, Error>(stream) })?)) } pub fn stdin(buffered: bool) -> Result { Ok(Self(apr::pool::PooledPtr::initialize(|pool| { let mut stream = std::ptr::null_mut(); let err = unsafe { crate::generated::svn_stream_for_stdin2( &mut stream, buffered as i32, pool.as_mut_ptr(), ) }; Error::from_raw(err)?; Ok::<_, Error>(stream) })?)) } pub fn stderr() -> Result { Ok(Self(apr::pool::PooledPtr::initialize(|pool| { let mut stream = std::ptr::null_mut(); let err = unsafe { crate::generated::svn_stream_for_stderr(&mut stream, pool.as_mut_ptr()) }; Error::from_raw(err)?; Ok::<_, Error>(stream) })?)) } pub fn stdout() -> Result { Ok(Self(apr::pool::PooledPtr::initialize(|pool| { let mut stream = std::ptr::null_mut(); let err = unsafe { crate::generated::svn_stream_for_stdout(&mut stream, pool.as_mut_ptr()) }; Error::from_raw(err)?; Ok::<_, Error>(stream) })?)) } pub fn buffered() -> Self { Self( apr::pool::PooledPtr::initialize(|pool| { let stream = unsafe { crate::generated::svn_stream_buffered(pool.as_mut_ptr()) }; Ok::<_, crate::Error>(stream) }) .unwrap(), ) } pub fn compressed(&mut self) -> Self { Self( apr::pool::PooledPtr::initialize(|pool| { let stream = unsafe { crate::generated::svn_stream_compressed(self.0.as_mut_ptr(), pool.as_mut_ptr()) }; Ok::<_, crate::Error>(stream) }) .unwrap(), ) } pub fn supports_partial_read(&mut self) -> bool { unsafe { crate::generated::svn_stream_supports_partial_read(self.0.as_mut_ptr()) != 0 } } pub fn supports_mark(&mut self) -> bool { unsafe { crate::generated::svn_stream_supports_mark(self.0.as_mut_ptr()) != 0 } } pub fn supports_reset(&mut self) -> bool { unsafe { crate::generated::svn_stream_supports_reset(self.0.as_mut_ptr()) != 0 } } pub fn data_available(&mut self) -> Result { let mut data_available = 0; let err = unsafe { crate::generated::svn_stream_data_available(self.0.as_mut_ptr(), &mut data_available) }; Error::from_raw(err)?; Ok(data_available != 0) } pub fn puts(&mut self, s: &str) -> Result<(), Error> { let s = std::ffi::CString::new(s).unwrap(); let err = unsafe { crate::generated::svn_stream_puts(self.0.as_mut_ptr(), s.as_ptr()) }; Error::from_raw(err)?; Ok(()) } } impl std::io::Write for Stream { fn write(&mut self, buf: &[u8]) -> std::io::Result { let mut len = 0; let err = unsafe { crate::generated::svn_stream_write( self.0.as_mut_ptr(), buf.as_ptr() as *const i8, &mut len, ) }; Error::from_raw(err)?; Ok(len) } fn flush(&mut self) -> std::io::Result<()> { Ok(()) } } impl std::io::Read for Stream { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { let mut len = 0; let err = unsafe { crate::generated::svn_stream_read( self.0.as_mut_ptr(), buf.as_mut_ptr() as *mut i8, &mut len, ) }; Error::from_raw(err)?; Ok(len) } } pub fn tee(out1: &mut Stream, out2: &mut Stream) -> Result { Ok(Stream(apr::pool::PooledPtr::initialize(|pool| { let stream = unsafe { crate::generated::svn_stream_tee( out1.0.as_mut_ptr(), out2.0.as_mut_ptr(), pool.as_mut_ptr(), ) }; Ok::<_, crate::Error>(stream) })?)) } impl From<&[u8]> for Stream { fn from(bytes: &[u8]) -> Self { Self( apr::pool::PooledPtr::initialize(|pool| { let buf = crate::generated::svn_string_t { data: bytes.as_ptr() as *mut i8, len: bytes.len(), }; let stream = unsafe { crate::generated::svn_stream_from_string(&buf, pool.as_mut_ptr()) }; Ok::<_, crate::Error>(stream) }) .unwrap(), ) } } pub fn remove_file(path: &std::path::Path, ignore_enoent: bool) -> Result<(), Error> { let path = std::ffi::CString::new(path.to_str().unwrap()).unwrap(); let err = unsafe { crate::generated::svn_io_remove_file2( path.as_ptr(), ignore_enoent as i32, apr::pool::Pool::new().as_mut_ptr(), ) }; Error::from_raw(err)?; Ok(()) } pub fn wrap_write(write: &mut dyn std::io::Write) -> Result { let write = Box::into_raw(Box::new(write)); let mut stream = apr::pool::PooledPtr::initialize(|pool| { let stream = unsafe { crate::generated::svn_stream_create(write as *mut std::ffi::c_void, pool.as_mut_ptr()) }; Ok::<_, Error>(stream) })?; extern "C" fn write_fn( baton: *mut std::ffi::c_void, buffer: *const i8, len: *mut usize, ) -> *mut crate::generated::svn_error_t { let write = unsafe { Box::from_raw(baton as *mut &mut dyn std::io::Write) }; let write = Box::leak(write); let buffer = unsafe { std::slice::from_raw_parts(buffer as *const u8, *len) }; match write.write(buffer) { Ok(_) => std::ptr::null_mut(), Err(e) => { let mut e: crate::Error = e.into(); unsafe { e.detach() } } } } extern "C" fn close_fn(baton: *mut std::ffi::c_void) -> *mut crate::generated::svn_error_t { let write = unsafe { Box::from_raw(baton as *mut &mut dyn std::io::Write) }; match write.flush() { Ok(_) => std::ptr::null_mut(), Err(e) => { let mut e: crate::Error = e.into(); unsafe { e.detach() } } } } unsafe { crate::generated::svn_stream_set_write(stream.as_mut_ptr(), Some(write_fn)); crate::generated::svn_stream_set_close(stream.as_mut_ptr(), Some(close_fn)); } Ok(Stream(stream)) } subversion-0.0.3/src/lib.rs000064400000000000000000000250431046102023000137320ustar 00000000000000pub mod client; pub mod config; pub mod dirent; pub mod error; pub mod fs; mod generated; pub mod io; pub mod mergeinfo; pub mod props; pub mod ra; pub mod repos; pub mod time; pub mod uri; pub mod version; pub mod wc; use crate::generated::{svn_opt_revision_t, svn_opt_revision_value_t}; use apr::pool::PooledPtr; use std::str::FromStr; pub use version::Version; pub type Revnum = generated::svn_revnum_t; pub use error::Error; #[derive(Debug, Default, Clone, Copy)] pub enum Revision { #[default] Unspecified, Number(Revnum), Date(i64), Committed, Previous, Base, Working, Head, } impl FromStr for Revision { type Err = String; fn from_str(rev: &str) -> Result { if rev == "unspecified" { Ok(Revision::Unspecified) } else if rev == "committed" { Ok(Revision::Committed) } else if rev == "previous" { Ok(Revision::Previous) } else if rev == "base" { Ok(Revision::Base) } else if rev == "working" { Ok(Revision::Working) } else if rev == "head" { Ok(Revision::Head) } else if let Some(rest) = rev.strip_prefix("number:") { Ok(Revision::Number( rest.parse::().map_err(|e| e.to_string())?, )) } else if let Some(rest) = rev.strip_prefix("date:") { Ok(Revision::Date( rest.parse::().map_err(|e| e.to_string())?, )) } else { Err(format!("Invalid revision: {}", rev)) } } } #[cfg(feature = "pyo3")] impl pyo3::FromPyObject<'_> for Revision { fn extract(ob: &pyo3::PyAny) -> pyo3::PyResult { if ob.is_instance_of::() { let rev = ob.extract::()?; return Revision::from_str(&rev).map_err(|e| { pyo3::exceptions::PyValueError::new_err(format!("Invalid revision: {}", e)) }); } else if ob.is_instance_of::() { let rev = ob.extract::()?; return Ok(Revision::Number(rev as Revnum)); } else { Err(pyo3::exceptions::PyTypeError::new_err(format!( "Invalid revision: {:?}", ob ))) } } } impl From for Revision { fn from(revnum: Revnum) -> Self { Revision::Number(revnum) } } impl From for svn_opt_revision_t { fn from(revision: Revision) -> Self { match revision { Revision::Unspecified => svn_opt_revision_t { kind: generated::svn_opt_revision_kind_svn_opt_revision_unspecified, value: svn_opt_revision_value_t::default(), }, Revision::Number(revnum) => { let mut uf = crate::generated::__BindgenUnionField::::new(); unsafe { *uf.as_mut() = revnum; } svn_opt_revision_t { kind: generated::svn_opt_revision_kind_svn_opt_revision_number, value: svn_opt_revision_value_t { number: uf, ..Default::default() }, } } Revision::Date(date) => { let mut uf = crate::generated::__BindgenUnionField::::new(); unsafe { *uf.as_mut() = date; } svn_opt_revision_t { kind: generated::svn_opt_revision_kind_svn_opt_revision_date, value: svn_opt_revision_value_t { date: uf, ..Default::default() }, } } Revision::Committed => svn_opt_revision_t { kind: generated::svn_opt_revision_kind_svn_opt_revision_committed, value: svn_opt_revision_value_t::default(), }, Revision::Previous => svn_opt_revision_t { kind: generated::svn_opt_revision_kind_svn_opt_revision_previous, value: svn_opt_revision_value_t::default(), }, Revision::Base => svn_opt_revision_t { kind: generated::svn_opt_revision_kind_svn_opt_revision_base, value: svn_opt_revision_value_t::default(), }, Revision::Working => svn_opt_revision_t { kind: generated::svn_opt_revision_kind_svn_opt_revision_working, value: svn_opt_revision_value_t::default(), }, Revision::Head => svn_opt_revision_t { kind: generated::svn_opt_revision_kind_svn_opt_revision_head, value: svn_opt_revision_value_t::default(), }, } } } #[derive(Debug, Clone, Copy, Default)] pub enum Depth { #[default] Unknown, Exclude, Empty, Files, Immediates, Infinity, } impl From for generated::svn_depth_t { fn from(depth: Depth) -> Self { match depth { Depth::Unknown => generated::svn_depth_t_svn_depth_unknown, Depth::Exclude => generated::svn_depth_t_svn_depth_exclude, Depth::Empty => generated::svn_depth_t_svn_depth_empty, Depth::Files => generated::svn_depth_t_svn_depth_files, Depth::Immediates => generated::svn_depth_t_svn_depth_immediates, Depth::Infinity => generated::svn_depth_t_svn_depth_infinity, } } } impl std::str::FromStr for Depth { type Err = String; fn from_str(depth: &str) -> Result { match depth { "unknown" => Ok(Depth::Unknown), "exclude" => Ok(Depth::Exclude), "empty" => Ok(Depth::Empty), "files" => Ok(Depth::Files), "immediates" => Ok(Depth::Immediates), "infinity" => Ok(Depth::Infinity), _ => Err(format!("Invalid depth: {}", depth)), } } } #[cfg(feature = "pyo3")] impl pyo3::FromPyObject<'_> for Depth { fn extract(ob: &pyo3::PyAny) -> pyo3::PyResult { if ob.is_instance_of::() { let depth = ob.extract::()?; return Depth::from_str(&depth).map_err(|e| { pyo3::exceptions::PyValueError::new_err(format!("Invalid depth: {}", e)) }); } else if ob.is_instance_of::() { let depth = ob.extract::()?; return Ok(if depth { Depth::Infinity } else { Depth::Empty }); } else { Err(pyo3::exceptions::PyTypeError::new_err(format!( "Invalid depth: {:?}", ob ))) } } } pub struct CommitInfo(PooledPtr); impl CommitInfo { pub fn revision(&self) -> Revnum { self.0.revision } pub fn date(&self) -> &str { unsafe { let date = self.0.date; std::ffi::CStr::from_ptr(date).to_str().unwrap() } } pub fn author(&self) -> &str { unsafe { let author = self.0.author; std::ffi::CStr::from_ptr(author).to_str().unwrap() } } pub fn post_commit_err(&self) -> Option<&str> { unsafe { let err = self.0.post_commit_err; if err.is_null() { None } else { Some(std::ffi::CStr::from_ptr(err).to_str().unwrap()) } } } pub fn repos_root(&self) -> &str { unsafe { let repos_root = self.0.repos_root; std::ffi::CStr::from_ptr(repos_root).to_str().unwrap() } } } impl Clone for CommitInfo { fn clone(&self) -> Self { unsafe { Self( PooledPtr::initialize(|pool| { Ok::<_, Error>(crate::generated::svn_commit_info_dup( self.0.as_ptr(), pool.as_mut_ptr(), )) }) .unwrap(), ) } } } pub struct RevisionRange { pub start: Revision, pub end: Revision, } impl From<&RevisionRange> for generated::svn_opt_revision_range_t { fn from(range: &RevisionRange) -> Self { Self { start: range.start.into(), end: range.end.into(), } } } impl RevisionRange { pub unsafe fn to_c(&self, pool: &mut apr::Pool) -> *mut generated::svn_opt_revision_range_t { let range = pool.calloc::(); *range = self.into(); range } } pub struct LogEntry(apr::pool::PooledPtr); pub type FileSize = crate::generated::svn_filesize_t; #[derive(Debug, Clone, Copy, Default)] pub enum NativeEOL { #[default] Standard, LF, CRLF, CR, } impl TryFrom> for NativeEOL { type Error = crate::Error; fn try_from(eol: Option<&str>) -> Result { match eol { None => Ok(NativeEOL::Standard), Some("LF") => Ok(NativeEOL::LF), Some("CRLF") => Ok(NativeEOL::CRLF), Some("CR") => Ok(NativeEOL::CR), _ => Err(crate::Error::new( crate::generated::svn_errno_t_SVN_ERR_IO_UNKNOWN_EOL.into(), None, "Unknown eol marker", )), } } } impl From for Option<&str> { fn from(eol: NativeEOL) -> Self { match eol { NativeEOL::Standard => None, NativeEOL::LF => Some("LF"), NativeEOL::CRLF => Some("CRLF"), NativeEOL::CR => Some("CR"), } } } pub struct InheritedItem(apr::pool::PooledPtr); impl InheritedItem { pub fn from_raw(ptr: apr::pool::PooledPtr) -> Self { Self(ptr) } pub fn path_or_url(&self) -> &str { unsafe { let path_or_url = self.0.path_or_url; std::ffi::CStr::from_ptr(path_or_url).to_str().unwrap() } } } pub struct Canonical(T); impl std::ops::Deref for Canonical { type Target = T; fn deref(&self) -> &Self::Target { &self.0 } } impl std::fmt::Debug for Canonical { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_tuple("Canonical").field(&self.to_string()).finish() } } impl PartialEq for Canonical where T: PartialEq, { fn eq(&self, other: &Self) -> bool { self.0.eq(&other.0) } } impl Eq for Canonical where T: Eq {} subversion-0.0.3/src/main.rs000064400000000000000000000000551046102023000141040ustar 00000000000000fn main() { println!("Hello, world!"); } subversion-0.0.3/src/mergeinfo.rs000064400000000000000000000000001046102023000151210ustar 00000000000000subversion-0.0.3/src/path.rs000064400000000000000000000000001046102023000141020ustar 00000000000000subversion-0.0.3/src/props.rs000064400000000000000000000042131046102023000143230ustar 00000000000000#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Kind { Entry, Wc, Regular, } impl From for Kind { fn from(kind: crate::generated::svn_prop_kind_t) -> Self { match kind { crate::generated::svn_prop_kind_svn_prop_entry_kind => Kind::Entry, crate::generated::svn_prop_kind_svn_prop_wc_kind => Kind::Wc, crate::generated::svn_prop_kind_svn_prop_regular_kind => Kind::Regular, _ => panic!("Unknown property kind"), } } } pub fn kind(name: &str) -> Kind { let name = std::ffi::CString::new(name).unwrap(); unsafe { crate::generated::svn_property_kind2(name.as_ptr()) }.into() } pub fn is_svn_prop(name: &str) -> bool { let name = std::ffi::CString::new(name).unwrap(); unsafe { crate::generated::svn_prop_is_svn_prop(name.as_ptr()) != 0 } } pub fn is_boolean(name: &str) -> bool { let name = std::ffi::CString::new(name).unwrap(); unsafe { crate::generated::svn_prop_is_boolean(name.as_ptr()) != 0 } } pub fn is_known_svn_rev_prop(name: &str) -> bool { let name = std::ffi::CString::new(name).unwrap(); unsafe { crate::generated::svn_prop_is_known_svn_rev_prop(name.as_ptr()) != 0 } } pub fn is_known_svn_node_prop(name: &str) -> bool { let name = std::ffi::CString::new(name).unwrap(); unsafe { crate::generated::svn_prop_is_known_svn_node_prop(name.as_ptr()) != 0 } } pub fn is_known_svn_file_prop(name: &str) -> bool { let name = std::ffi::CString::new(name).unwrap(); unsafe { crate::generated::svn_prop_is_known_svn_file_prop(name.as_ptr()) != 0 } } pub fn is_known_svn_dir_prop(name: &str) -> bool { let name = std::ffi::CString::new(name).unwrap(); unsafe { crate::generated::svn_prop_is_known_svn_dir_prop(name.as_ptr()) != 0 } } pub fn needs_translation(name: &str) -> bool { let name = std::ffi::CString::new(name).unwrap(); unsafe { crate::generated::svn_prop_needs_translation(name.as_ptr()) != 0 } } pub fn name_is_valid(name: &str) -> bool { let name = std::ffi::CString::new(name).unwrap(); unsafe { crate::generated::svn_prop_name_is_valid(name.as_ptr()) != 0 } } subversion-0.0.3/src/ra.rs000064400000000000000000000122551046102023000135670ustar 00000000000000use crate::generated::svn_ra_session_t; use crate::{Error, Revnum}; use apr::pool::{Pool, PooledPtr}; pub struct Session(PooledPtr); impl Session { pub fn reparent(&mut self, url: &str) -> Result<(), Error> { let url = std::ffi::CString::new(url).unwrap(); let mut pool = Pool::new(); let err = unsafe { crate::generated::svn_ra_reparent(self.0.as_mut_ptr(), url.as_ptr(), pool.as_mut_ptr()) }; Error::from_raw(err)?; Ok(()) } pub fn from_raw(raw: PooledPtr) -> Self { Self(raw) } pub fn get_session_url(&mut self) -> Result { let mut pool = Pool::new(); let mut url = std::ptr::null(); let err = unsafe { crate::generated::svn_ra_get_session_url( self.0.as_mut_ptr(), &mut url, pool.as_mut_ptr(), ) }; Error::from_raw(err)?; let url = unsafe { std::ffi::CStr::from_ptr(url) }; Ok(url.to_string_lossy().into_owned()) } pub fn get_path_relative_to_session(&mut self, url: &str) -> Result { let url = std::ffi::CString::new(url).unwrap(); let mut pool = Pool::new(); let mut path = std::ptr::null(); let err = unsafe { crate::generated::svn_ra_get_path_relative_to_session( self.0.as_mut_ptr(), &mut path, url.as_ptr(), pool.as_mut_ptr(), ) }; Error::from_raw(err)?; let path = unsafe { std::ffi::CStr::from_ptr(path) }; Ok(path.to_string_lossy().into_owned()) } pub fn get_latest_revnum(&mut self) -> Result { let mut pool = Pool::new(); let mut revnum = 0; let err = unsafe { crate::generated::svn_ra_get_latest_revnum( self.0.as_mut_ptr(), &mut revnum, pool.as_mut_ptr(), ) }; Error::from_raw(err)?; Ok(revnum) } pub fn get_dated_revision(&mut self, tm: impl apr::time::IntoTime) -> Result { let mut pool = Pool::new(); let mut revnum = 0; let err = unsafe { crate::generated::svn_ra_get_dated_revision( self.0.as_mut_ptr(), &mut revnum, tm.as_apr_time().into(), pool.as_mut_ptr(), ) }; Error::from_raw(err)?; Ok(revnum) } pub fn change_revprop( &mut self, rev: Revnum, name: &str, old_value: Option<&[u8]>, new_value: &[u8], ) -> Result<(), Error> { let name = std::ffi::CString::new(name).unwrap(); let mut pool = Pool::new(); let new_value = crate::generated::svn_string_t { data: new_value.as_ptr() as *mut _, len: new_value.len(), }; let old_value = old_value.map(|v| crate::generated::svn_string_t { data: v.as_ptr() as *mut _, len: v.len(), }); let err = unsafe { crate::generated::svn_ra_change_rev_prop2( self.0.as_mut_ptr(), rev, name.as_ptr(), &old_value.map_or(std::ptr::null(), |v| &v), &new_value, pool.as_mut_ptr(), ) }; Error::from_raw(err)?; Ok(()) } pub fn rev_proplist( &mut self, rev: Revnum, ) -> Result>, Error> { let mut pool = Pool::new(); let mut props = std::ptr::null_mut(); let err = unsafe { crate::generated::svn_ra_rev_proplist( self.0.as_mut_ptr(), rev, &mut props, pool.as_mut_ptr(), ) }; let mut hash = apr::hash::Hash::<&str, *const crate::generated::svn_string_t>::from_raw(unsafe { PooledPtr::in_pool(std::rc::Rc::new(pool), props) }); Error::from_raw(err)?; Ok(hash .iter() .map(|(k, v)| { ( String::from_utf8_lossy(k).into_owned(), Vec::from(unsafe { std::slice::from_raw_parts((**v).data as *const u8, (**v).len) }), ) }) .collect()) } pub fn rev_prop(&mut self, rev: Revnum, name: &str) -> Result>, Error> { let name = std::ffi::CString::new(name).unwrap(); let mut pool = Pool::new(); let mut value = std::ptr::null_mut(); let err = unsafe { crate::generated::svn_ra_rev_prop( self.0.as_mut_ptr(), rev, name.as_ptr(), &mut value, pool.as_mut_ptr(), ) }; Error::from_raw(err)?; if value.is_null() { Ok(None) } else { Ok(Some(Vec::from(unsafe { std::slice::from_raw_parts((*value).data as *const u8, (*value).len) }))) } } } subversion-0.0.3/src/repos.rs000064400000000000000000000232041046102023000143110ustar 00000000000000use crate::generated::{svn_repos_create, svn_repos_find_root_path, svn_repos_t}; use crate::Error; use apr::pool::PooledPtr; pub fn find_root_path(path: &std::path::Path) -> Option { let path = std::ffi::CString::new(path.to_str().unwrap()).unwrap(); let mut pool = apr::Pool::new(); let ret = unsafe { svn_repos_find_root_path(path.as_ptr(), pool.as_mut_ptr()) }; if ret.is_null() { let root_path = unsafe { std::ffi::CStr::from_ptr(ret) }; Some(std::path::PathBuf::from(root_path.to_str().unwrap())) } else { None } } pub struct Repos(PooledPtr); impl Repos { pub fn create(path: &std::path::Path) -> Result { // TODO: Support config, fs_config let path = std::ffi::CString::new(path.to_str().unwrap()).unwrap(); let config = std::ptr::null_mut(); let fs_config = std::ptr::null_mut(); Ok(Self(PooledPtr::initialize(|pool| unsafe { let mut repos: *mut svn_repos_t = std::ptr::null_mut(); let ret = svn_repos_create( &mut repos, path.as_ptr(), std::ptr::null(), std::ptr::null(), config, fs_config, pool.as_mut_ptr(), ); Error::from_raw(ret)?; Ok::<_, Error>(repos) })?)) } pub fn open(path: &std::path::Path) -> Result { let path = std::ffi::CString::new(path.to_str().unwrap()).unwrap(); Ok(Self(PooledPtr::initialize(|pool| { let mut repos: *mut svn_repos_t = std::ptr::null_mut(); let ret = unsafe { crate::generated::svn_repos_open(&mut repos, path.as_ptr(), pool.as_mut_ptr()) }; Error::from_raw(ret)?; Ok::<_, Error>(repos) })?)) } pub fn capabilities(&mut self) -> Result, crate::Error> { let mut capabilities = apr::hash::Hash::<&str, &str>::from_raw(PooledPtr::initialize(|pool| { let mut capabilities: *mut apr::hash::apr_hash_t = std::ptr::null_mut(); let mut scratch_pool = apr::Pool::new(); let ret = unsafe { crate::generated::svn_repos_capabilities( &mut capabilities, self.0.as_mut_ptr(), pool.as_mut_ptr(), scratch_pool.as_mut_ptr(), ) }; Error::from_raw(ret)?; Ok::<_, Error>(capabilities) })?); Ok(capabilities .keys() .map(|k| String::from_utf8_lossy(k).to_string()) .collect::>()) } pub fn has_capability(&mut self, capability: &str) -> Result { let capability = std::ffi::CString::new(capability).unwrap(); let mut pool = apr::Pool::new(); unsafe { let mut has: crate::generated::svn_boolean_t = 0; let ret = crate::generated::svn_repos_has_capability( self.0.as_mut_ptr(), &mut has, capability.as_ptr(), pool.as_mut_ptr(), ); Error::from_raw(ret)?; Ok(has != 0) } } pub fn remember_client_capabilities( &mut self, capabilities: &[&str], ) -> Result<(), crate::Error> { let capabilities = capabilities .iter() .map(|c| std::ffi::CString::new(*c).unwrap()) .collect::>(); let capabilities_array = capabilities .iter() .map(|c| c.as_ptr()) .collect::>(); let ret = unsafe { crate::generated::svn_repos_remember_client_capabilities( self.0.as_mut_ptr(), capabilities_array.as_ptr(), ) }; Error::from_raw(ret)?; Ok(()) } pub fn fs(&mut self) -> Result { unsafe { Ok(crate::fs::Fs(PooledPtr::in_pool( self.0.pool(), crate::generated::svn_repos_fs(self.0.as_mut_ptr()), ))) } } pub fn fs_type(&mut self) -> String { let mut pool = apr::Pool::new(); let ret = unsafe { crate::generated::svn_repos_fs_type(self.0.as_mut_ptr(), pool.as_mut_ptr()) }; let fs_type = unsafe { std::ffi::CStr::from_ptr(ret) }; fs_type.to_str().unwrap().to_string() } pub fn path(&mut self) -> std::path::PathBuf { let mut pool = apr::Pool::new(); let ret = unsafe { crate::generated::svn_repos_path(self.0.as_mut_ptr(), pool.as_mut_ptr()) }; let path = unsafe { std::ffi::CStr::from_ptr(ret) }; std::path::PathBuf::from(path.to_str().unwrap()) } pub fn db_env(&mut self) -> std::path::PathBuf { let mut pool = apr::Pool::new(); let ret = unsafe { crate::generated::svn_repos_db_env(self.0.as_mut_ptr(), pool.as_mut_ptr()) }; let db_env = unsafe { std::ffi::CStr::from_ptr(ret) }; std::path::PathBuf::from(db_env.to_str().unwrap()) } pub fn conf_dir(&mut self) -> std::path::PathBuf { let mut pool = apr::Pool::new(); let ret = unsafe { crate::generated::svn_repos_conf_dir(self.0.as_mut_ptr(), pool.as_mut_ptr()) }; let conf_dir = unsafe { std::ffi::CStr::from_ptr(ret) }; std::path::PathBuf::from(conf_dir.to_str().unwrap()) } pub fn svnserve_conf(&mut self) -> std::path::PathBuf { let mut pool = apr::Pool::new(); let ret = unsafe { crate::generated::svn_repos_svnserve_conf(self.0.as_mut_ptr(), pool.as_mut_ptr()) }; let svnserve_conf = unsafe { std::ffi::CStr::from_ptr(ret) }; std::path::PathBuf::from(svnserve_conf.to_str().unwrap()) } pub fn lock_dir(&mut self) -> std::path::PathBuf { let mut pool = apr::Pool::new(); let ret = unsafe { crate::generated::svn_repos_lock_dir(self.0.as_mut_ptr(), pool.as_mut_ptr()) }; let lock_dir = unsafe { std::ffi::CStr::from_ptr(ret) }; std::path::PathBuf::from(lock_dir.to_str().unwrap()) } pub fn db_lockfile(&mut self) -> std::path::PathBuf { let mut pool = apr::Pool::new(); let ret = unsafe { crate::generated::svn_repos_db_lockfile(self.0.as_mut_ptr(), pool.as_mut_ptr()) }; let db_lockfile = unsafe { std::ffi::CStr::from_ptr(ret) }; std::path::PathBuf::from(db_lockfile.to_str().unwrap()) } pub fn db_logs_lockfile(&mut self) -> std::path::PathBuf { let mut pool = apr::Pool::new(); let ret = unsafe { crate::generated::svn_repos_db_logs_lockfile(self.0.as_mut_ptr(), pool.as_mut_ptr()) }; let logs_lockfile = unsafe { std::ffi::CStr::from_ptr(ret) }; std::path::PathBuf::from(logs_lockfile.to_str().unwrap()) } pub fn hook_dir(&mut self) -> std::path::PathBuf { let mut pool = apr::Pool::new(); let ret = unsafe { crate::generated::svn_repos_hook_dir(self.0.as_mut_ptr(), pool.as_mut_ptr()) }; let hook_dir = unsafe { std::ffi::CStr::from_ptr(ret) }; std::path::PathBuf::from(hook_dir.to_str().unwrap()) } } pub struct Notify(PooledPtr); extern "C" fn wrap_notify_func( baton: *mut std::ffi::c_void, notify: *const crate::generated::svn_repos_notify_t, pool: *mut apr::apr_pool_t, ) { let pool = apr::Pool::from_raw(pool); let notify = unsafe { &*notify }; let baton = unsafe { &mut *(baton as *mut Box) }; unsafe { baton(&Notify(PooledPtr::in_pool( std::rc::Rc::new(pool), notify as *const _ as *mut _, ))); } } pub fn upgrade( path: &std::path::Path, nonblocking: bool, notify_func: Option<&mut dyn FnMut(&Notify)>, ) -> Result<(), crate::Error> { let path = std::ffi::CString::new(path.to_str().unwrap()).unwrap(); let notify_func = notify_func.map(|_notify_func| wrap_notify_func as _); let notify_baton = Box::into_raw(Box::new(notify_func)).cast(); let mut pool = apr::Pool::new(); let ret = unsafe { crate::generated::svn_repos_upgrade2( path.as_ptr(), nonblocking as i32, notify_func, notify_baton, pool.as_mut_ptr(), ) }; Error::from_raw(ret)?; Ok(()) } pub fn delete(path: &std::path::Path) -> Result<(), crate::Error> { let path = std::ffi::CString::new(path.to_str().unwrap()).unwrap(); let mut pool = apr::Pool::new(); let ret = unsafe { crate::generated::svn_repos_delete(path.as_ptr(), pool.as_mut_ptr()) }; Error::from_raw(ret)?; Ok(()) } #[cfg(test)] mod tests { #[test] fn test_create_delete() { let td = tempfile::tempdir().unwrap(); super::Repos::create(td.path()).unwrap(); super::Repos::open(td.path()).unwrap(); super::delete(td.path()).unwrap(); } #[test] fn test_capabilities() { let td = tempfile::tempdir().unwrap(); let mut repos = super::Repos::create(td.path()).unwrap(); assert!(repos.capabilities().unwrap().contains("mergeinfo")); assert!(!repos.capabilities().unwrap().contains("mergeinfo2")); assert!(repos.has_capability("mergeinfo").unwrap()); assert!(repos.has_capability("unknown").is_err()); } } pub fn version() -> crate::Version { unsafe { crate::Version(crate::generated::svn_client_version()) } } subversion-0.0.3/src/time.rs000064400000000000000000000026211046102023000141170ustar 00000000000000use crate::generated::{svn_time_from_cstring, svn_time_to_cstring, svn_time_to_human_cstring}; use crate::Error; use apr::time::Time; pub fn to_cstring(time: Time) -> String { let x = unsafe { svn_time_to_cstring(time.into(), apr::pool::Pool::new().as_mut_ptr()) }; let s = unsafe { std::ffi::CStr::from_ptr(x) }; s.to_string_lossy().into_owned() } pub fn from_cstring(s: &str) -> Result { let mut t = apr::apr_time_t::default(); let s = std::ffi::CString::new(s).unwrap(); let err = unsafe { svn_time_from_cstring(&mut t, s.as_ptr(), apr::pool::Pool::new().as_mut_ptr()) }; Error::from_raw(err)?; Ok(Time::from(t)) } pub fn to_human_cstring(time: Time) -> String { let x = unsafe { svn_time_to_human_cstring(time.into(), apr::pool::Pool::new().as_mut_ptr()) }; let s = unsafe { std::ffi::CStr::from_ptr(x) }; s.to_string_lossy().into_owned() } pub fn parse_date(now: Time, date: &str) -> Result<(bool, Time), crate::Error> { let mut t = apr::apr_time_t::default(); let mut matched: i32 = 0; let date = std::ffi::CString::new(date).unwrap(); let err = unsafe { crate::generated::svn_parse_date( &mut matched, &mut t, date.as_ptr(), now.into(), apr::pool::Pool::new().as_mut_ptr(), ) }; Error::from_raw(err)?; Ok((matched != 0, Time::from(t))) } subversion-0.0.3/src/uri.rs000064400000000000000000000166471046102023000137750ustar 00000000000000use crate::dirent::Dirent; use crate::Canonical; use crate::Error; use apr::pool::Pooled; pub struct Uri<'a>(*const i8, std::marker::PhantomData<&'a ()>); impl ToString for Uri<'_> { fn to_string(&self) -> String { let t = unsafe { std::ffi::CStr::from_ptr(self.as_ptr()) }; t.to_str().unwrap().to_string() } } impl std::fmt::Debug for Uri<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_tuple("Uri").field(&self.to_string()).finish() } } impl PartialEq for Uri<'_> { fn eq(&self, other: &Self) -> bool { self.to_string() == other.to_string() } } impl Eq for Uri<'_> {} impl<'a> Uri<'a> { pub fn from_raw(raw: *const i8) -> Self { Self(raw, std::marker::PhantomData) } pub fn from_cstr(cstr: &std::ffi::CStr) -> Self { Self(cstr.as_ptr(), std::marker::PhantomData) } pub fn pooled(s: &str) -> Pooled { Pooled::initialize(|pool| { Ok::<_, crate::Error>(Self( apr::strings::pstrdup(s, pool), std::marker::PhantomData, )) }) .unwrap() } pub fn is_root(&self, length: usize) -> bool { unsafe { crate::generated::svn_uri_is_root(self.as_ptr(), length) != 0 } } pub fn as_ptr(&self) -> *const i8 { self.0 } pub fn dirname(&self) -> Pooled { Pooled::initialize(|pool| unsafe { Ok::<_, crate::Error>(Self( crate::generated::svn_uri_dirname(self.as_ptr(), pool.as_mut_ptr()) as *const i8, std::marker::PhantomData, )) }) .unwrap() } pub fn basename(&self) -> Pooled { Pooled::initialize(|pool| unsafe { Ok::<_, crate::Error>(Self( crate::generated::svn_uri_basename(self.as_ptr(), pool.as_mut_ptr()), std::marker::PhantomData, )) }) .unwrap() } pub fn split(&self) -> (Pooled, Pooled) { let mut pool = apr::pool::Pool::new(); unsafe { let mut dirname = std::ptr::null(); let mut basename = std::ptr::null(); crate::generated::svn_uri_split( &mut dirname, &mut basename, self.as_ptr(), pool.as_mut_ptr(), ); let pool = std::rc::Rc::new(pool); ( Pooled::in_pool(pool.clone(), Self(dirname, std::marker::PhantomData)), Pooled::in_pool(pool, Self(basename, std::marker::PhantomData)), ) } } pub fn canonicalize(&self) -> Pooled> { Pooled::initialize(|pool| unsafe { Ok::<_, crate::Error>(Canonical(Self( crate::generated::svn_uri_canonicalize(self.as_ptr(), pool.as_mut_ptr()), std::marker::PhantomData, ))) }) .unwrap() } pub fn canonicalize_in<'b>(&'_ self, pool: &'b mut apr::pool::Pool) -> Canonical where 'a: 'b, { Canonical(Self( unsafe { crate::generated::svn_uri_canonicalize(self.as_ptr(), pool.as_mut_ptr()) }, std::marker::PhantomData, )) } pub fn canonicalize_safe( &self, ) -> Result<(Pooled>, Pooled), crate::Error> { let mut pool = apr::pool::Pool::new(); unsafe { let mut canonical = std::ptr::null(); let mut non_canonical = std::ptr::null(); let err = crate::generated::svn_uri_canonicalize_safe( &mut canonical, &mut non_canonical, self.as_ptr(), pool.as_mut_ptr(), apr::pool::Pool::new().as_mut_ptr(), ); Error::from_raw(err)?; let pool = std::rc::Rc::new(pool); Ok(( Pooled::in_pool( pool.clone(), Canonical(Self(canonical, std::marker::PhantomData)), ), Pooled::in_pool(pool, Self(non_canonical, std::marker::PhantomData)), )) } } pub fn is_canonical(&self) -> bool { unsafe { crate::generated::svn_uri_is_canonical( self.as_ptr(), apr::pool::Pool::new().as_mut_ptr(), ) != 0 } } pub fn check_canonical(self) -> Option> { if self.is_canonical() { Some(Canonical(self)) } else { None } } fn to_dirent<'b>(&self) -> Result>, crate::Error> { Pooled::initialize(|pool| unsafe { let mut dirent = std::ptr::null(); let err = crate::generated::svn_uri_get_dirent_from_file_url( &mut dirent, self.as_ptr(), pool.as_mut_ptr(), ); Error::from_raw(err)?; Ok::<_, Error>(Dirent::from_raw(dirent)) }) } } impl<'a, 'b> TryFrom>> for Pooled> { type Error = crate::Error; fn try_from(uri: Canonical>) -> Result>, Self::Error> { uri.to_dirent() } } impl<'a> From<&'a str> for Uri<'a> { fn from(s: &'a str) -> Self { Self( std::ffi::CString::new(s).unwrap().into_raw(), std::marker::PhantomData, ) } } impl<'a> From<&Uri<'a>> for &'a str { fn from(uri: &Uri<'a>) -> Self { let t = unsafe { std::ffi::CStr::from_ptr(uri.as_ptr()) }; t.to_str().unwrap() } } #[cfg(feature = "url")] impl TryFrom for url::Url { type Error = crate::Error; fn try_from(uri: Uri) -> Result { let uri = uri.to_str()?; Ok(url::Url::parse(uri)?) } } #[cfg(feature = "url")] impl From for Uri<'_> { fn from(url: url::Url) -> Self { Self::new(url.as_str()) } } pub trait AsCanonicalUri<'a> { fn as_canonical_uri(self, scratch_pool: &mut apr::pool::Pool) -> Canonical>; } impl<'a> AsCanonicalUri<'a> for Uri<'a> { fn as_canonical_uri(self, scratch_pool: &mut apr::pool::Pool) -> Canonical> { self.canonicalize_in(scratch_pool) } } impl<'a> AsCanonicalUri<'a> for Canonical> { fn as_canonical_uri(self, _scratch_pool: &mut apr::pool::Pool) -> Canonical> { self } } #[cfg(feature = "url")] impl<'a> AsCanonicalUri<'a> for url::Url { fn as_canonical_uri(self, scratch_pool: &mut apr::pool::Pool) -> Canonical> { Uri::pooled(self.as_str()).canonicalize_in(scratch_pool) } } #[cfg(feature = "url")] impl<'a> AsCanonicalUri<'a> for &'a url::Url { fn as_canonical_uri(self, scratch_pool: &mut apr::pool::Pool) -> Canonical> { Uri::pooled(self.as_str()).canonicalize_in(scratch_pool) } } impl<'a> AsCanonicalUri<'a> for &'a str { fn as_canonical_uri(self, scratch_pool: &mut apr::pool::Pool) -> Canonical> { Uri::pooled(self).canonicalize_in(scratch_pool) } } impl<'a> AsCanonicalUri<'a> for Pooled> { fn as_canonical_uri(self, scratch_pool: &mut apr::pool::Pool) -> Canonical> { self.canonicalize_in(scratch_pool) } } impl<'a> AsCanonicalUri<'a> for Pooled>> { fn as_canonical_uri(self, _scratch_pool: &mut apr::pool::Pool) -> Canonical> { Canonical(Uri(self.0 .0, std::marker::PhantomData)) } } subversion-0.0.3/src/version.rs000064400000000000000000000020211046102023000146400ustar 00000000000000use crate::generated::svn_version_t; pub struct Version(pub(crate) *const svn_version_t); impl Version { fn equal(&self, other: &Version) -> bool { !matches!( unsafe { crate::generated::svn_ver_equal(self.0, other.0) }, 0 ) } pub fn compatible(&self, other: &Version) -> bool { !matches!( unsafe { crate::generated::svn_ver_compatible(self.0, other.0) }, 0 ) } pub fn major(&self) -> i32 { unsafe { self.0.as_ref().unwrap().major } } pub fn minor(&self) -> i32 { unsafe { self.0.as_ref().unwrap().minor } } pub fn patch(&self) -> i32 { unsafe { self.0.as_ref().unwrap().patch } } pub fn tag(&self) -> &str { unsafe { let tag = self.0.as_ref().unwrap().tag; std::ffi::CStr::from_ptr(tag).to_str().unwrap() } } } impl PartialEq for Version { fn eq(&self, other: &Version) -> bool { self.equal(other) } } impl Eq for Version {} subversion-0.0.3/src/wc.rs000064400000000000000000000111451046102023000135730ustar 00000000000000use crate::generated::{svn_wc_context_t, svn_wc_version}; use crate::Error; use apr::pool::PooledPtr; pub fn version() -> crate::Version { unsafe { crate::Version(svn_wc_version()) } } pub struct Context(apr::pool::PooledPtr); impl Context { pub fn new() -> Result { Ok(Self(PooledPtr::initialize(|pool| { let mut ctx = std::ptr::null_mut(); let err = unsafe { crate::generated::svn_wc_context_create( &mut ctx, std::ptr::null_mut(), pool.as_mut_ptr(), apr::pool::Pool::new().as_mut_ptr(), ) }; Error::from_raw(err)?; Ok::<_, Error>(ctx) })?)) } pub fn check_wc(&mut self, path: &str) -> Result { let path = std::ffi::CString::new(path).unwrap(); let mut wc_format = 0; let err = unsafe { crate::generated::svn_wc_check_wc2( &mut wc_format, self.0.as_mut_ptr(), path.as_ptr(), apr::pool::Pool::new().as_mut_ptr(), ) }; Error::from_raw(err)?; Ok(wc_format) } pub fn text_modified_p(&mut self, path: &str) -> Result { let path = std::ffi::CString::new(path).unwrap(); let mut modified = 0; let err = unsafe { crate::generated::svn_wc_text_modified_p2( &mut modified, self.0.as_mut_ptr(), path.as_ptr(), 0, apr::pool::Pool::new().as_mut_ptr(), ) }; Error::from_raw(err)?; Ok(modified != 0) } pub fn props_modified_p(&mut self, path: &str) -> Result { let path = std::ffi::CString::new(path).unwrap(); let mut modified = 0; let err = unsafe { crate::generated::svn_wc_props_modified_p2( &mut modified, self.0.as_mut_ptr(), path.as_ptr(), apr::pool::Pool::new().as_mut_ptr(), ) }; Error::from_raw(err)?; Ok(modified != 0) } pub fn conflicted_p(&mut self, path: &str) -> Result<(bool, bool, bool), crate::Error> { let path = std::ffi::CString::new(path).unwrap(); let mut text_conflicted = 0; let mut prop_conflicted = 0; let mut tree_conflicted = 0; let err = unsafe { crate::generated::svn_wc_conflicted_p3( &mut text_conflicted, &mut prop_conflicted, &mut tree_conflicted, self.0.as_mut_ptr(), path.as_ptr(), apr::pool::Pool::new().as_mut_ptr(), ) }; Error::from_raw(err)?; Ok(( text_conflicted != 0, prop_conflicted != 0, tree_conflicted != 0, )) } pub fn ensure_adm( &mut self, local_abspath: &str, url: &str, repos_root_url: &str, repos_uuid: &str, revision: crate::Revnum, depth: crate::Depth, ) -> Result<(), crate::Error> { let local_abspath = std::ffi::CString::new(local_abspath).unwrap(); let url = std::ffi::CString::new(url).unwrap(); let repos_root_url = std::ffi::CString::new(repos_root_url).unwrap(); let repos_uuid = std::ffi::CString::new(repos_uuid).unwrap(); let err = unsafe { crate::generated::svn_wc_ensure_adm4( self.0.as_mut_ptr(), local_abspath.as_ptr(), url.as_ptr(), repos_root_url.as_ptr(), repos_uuid.as_ptr(), revision, depth.into(), apr::pool::Pool::new().as_mut_ptr(), ) }; Error::from_raw(err)?; Ok(()) } } impl Drop for Context { fn drop(&mut self) { unsafe { crate::generated::svn_wc_context_destroy(self.0.as_mut_ptr()); } } } pub fn set_adm_dir(name: &str) -> Result<(), crate::Error> { let name = std::ffi::CString::new(name).unwrap(); let err = unsafe { crate::generated::svn_wc_set_adm_dir(name.as_ptr(), apr::pool::Pool::new().as_mut_ptr()) }; Error::from_raw(err)?; Ok(()) } pub fn get_adm_dir() -> String { let mut pool = apr::pool::Pool::new(); let name = unsafe { crate::generated::svn_wc_get_adm_dir(pool.as_mut_ptr()) }; unsafe { std::ffi::CStr::from_ptr(name) } .to_string_lossy() .into_owned() }