wadl-0.5.3/.cargo_vcs_info.json0000644000000001360000000000100117700ustar { "git": { "sha1": "0bd48798468f565e14505f47bba438780737baa1" }, "path_in_vcs": "" }wadl-0.5.3/.codespellrc000064400000000000000000000000461046102023000130600ustar 00000000000000[codespell] ignore-words-list = crate wadl-0.5.3/.github/CODEOWNERS000064400000000000000000000000121046102023000135040ustar 00000000000000* @jelmer wadl-0.5.3/.github/FUNDING.yml000064400000000000000000000000171046102023000137330ustar 00000000000000github: jelmer wadl-0.5.3/.github/dependabot.yml000064400000000000000000000007301046102023000147500ustar 00000000000000# Please see the documentation for all configuration options: # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates --- version: 2 updates: - package-ecosystem: "cargo" directory: "/" schedule: interval: "monthly" rebase-strategy: "disabled" groups: cargo: patterns: - "*" - package-ecosystem: "github-actions" directory: "/" schedule: interval: "monthly" wadl-0.5.3/.github/workflows/auto-merge.yaml000064400000000000000000000011341046102023000171050ustar 00000000000000name: Dependabot auto-merge on: pull_request_target permissions: pull-requests: write contents: write jobs: dependabot: runs-on: ubuntu-latest if: ${{ github.actor == 'dependabot[bot]' }} steps: - name: Dependabot metadata id: metadata uses: dependabot/fetch-metadata@v2 with: github-token: "${{ secrets.GITHUB_TOKEN }}" - name: Enable auto-merge for Dependabot PRs run: gh pr merge --auto --squash "$PR_URL" env: PR_URL: ${{github.event.pull_request.html_url}} GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} wadl-0.5.3/.github/workflows/disperse.yml000064400000000000000000000002741046102023000165210ustar 00000000000000--- name: Disperse configuration "on": - push jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - uses: jelmer/action-disperse-validate@v2 wadl-0.5.3/.github/workflows/rust.yml000064400000000000000000000010651046102023000156770ustar 00000000000000--- name: Rust "on": push: pull_request: env: CARGO_TERM_COLOR: always jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - run: cargo install cargo-deny - name: Install cargo-all-features run: cargo install cargo-all-features - name: Build run: cargo build --verbose env: RUSTFLAGS: -Dwarnings - name: Run tests run: cargo test-all-features env: RUSTFLAGS: -Dwarnings - name: Check formatting run: cargo fmt -- --check wadl-0.5.3/.gitignore000064400000000000000000000000121046102023000125410ustar 00000000000000*~ target wadl-0.5.3/CODE_OF_CONDUCT.md000064400000000000000000000125451046102023000133660ustar 00000000000000 # Contributor Covenant Code of Conduct ## Our Pledge We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socioeconomic status, nationality, personal appearance, race, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. ## Our Standards Examples of behavior that contributes to a positive environment for our community include: * Demonstrating empathy and kindness toward other people * Being respectful of differing opinions, viewpoints, and experiences * Giving and gracefully accepting constructive feedback * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience * Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: * The use of sexualized language or imagery, and sexual attention or advances of any kind * Trolling, insulting or derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or email address, without their explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. ## Scope This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official email address, posting via an official social media account, or acting as an appointed representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at [INSERT CONTACT METHOD]. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. ## Enforcement Guidelines Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: ### 1. Correction **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. ### 2. Warning **Community Impact**: A violation through a single incident or series of actions. **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. ### 3. Temporary Ban **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within the community. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at [https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0]. Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. For answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at [https://www.contributor-covenant.org/translations][translations]. [homepage]: https://www.contributor-covenant.org [v2.0]: https://www.contributor-covenant.org/version/2/0/code_of_conduct.html [Mozilla CoC]: https://github.com/mozilla/diversity [FAQ]: https://www.contributor-covenant.org/faq [translations]: https://www.contributor-covenant.org/translations wadl-0.5.3/Cargo.lock0000644000001527160000000000100077570ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "aho-corasick" version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] [[package]] name = "anstream" version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anstyle-parse" version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ "windows-sys 0.61.2", ] [[package]] name = "anstyle-wincon" version = "3.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", "windows-sys 0.61.2", ] [[package]] name = "async-trait" version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "atomic-waker" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bitflags" version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] name = "bumpalo" version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "bytes" version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" [[package]] name = "cc" version = "1.2.46" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97463e1064cb1b1c1384ad0a0b9c8abd0988e2a91f52606c80ef14aadb63e36" dependencies = [ "find-msvc-tools", "shlex", ] [[package]] name = "cesu8" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" [[package]] name = "cfg-if" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "clap" version = "4.5.52" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa8120877db0e5c011242f96806ce3c94e0737ab8108532a76a3300a01db2ab8" dependencies = [ "clap_builder", "clap_derive", ] [[package]] name = "clap_builder" version = "4.5.52" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02576b399397b659c26064fbc92a75fede9d18ffd5f80ca1cd74ddab167016e1" dependencies = [ "anstream", "anstyle", "clap_lex", "strsim", ] [[package]] name = "clap_derive" version = "4.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" dependencies = [ "heck", "proc-macro2", "quote", "syn", ] [[package]] name = "clap_lex" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" [[package]] name = "colorchoice" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "combine" version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" dependencies = [ "bytes", "memchr", ] [[package]] name = "core-foundation" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", ] [[package]] name = "core-foundation-sys" version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "displaydoc" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "encoding_rs" version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ "cfg-if", ] [[package]] name = "env_filter" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2" dependencies = [ "log", "regex", ] [[package]] name = "env_logger" version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" dependencies = [ "anstream", "anstyle", "env_filter", "jiff", "log", ] [[package]] name = "equivalent" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", "windows-sys 0.61.2", ] [[package]] name = "fastrand" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "find-msvc-tools" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foreign-types" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" dependencies = [ "foreign-types-shared", ] [[package]] name = "foreign-types-shared" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" dependencies = [ "percent-encoding", ] [[package]] name = "futf" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" dependencies = [ "mac", "new_debug_unreachable", ] [[package]] name = "futures-channel" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", ] [[package]] name = "futures-core" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-io" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-sink" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-core", "futures-io", "futures-sink", "futures-task", "memchr", "pin-project-lite", "pin-utils", "slab", ] [[package]] name = "getrandom" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", "wasi", ] [[package]] name = "getrandom" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "libc", "r-efi", "wasip2", ] [[package]] name = "h2" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" dependencies = [ "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", "http", "indexmap", "slab", "tokio", "tokio-util", "tracing", ] [[package]] name = "hashbrown" version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "html2md" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8cff9891f2e0d9048927fbdfc28b11bf378f6a93c7ba70b23d0fbee9af6071b4" dependencies = [ "html5ever", "jni", "lazy_static", "markup5ever_rcdom", "percent-encoding", "regex", ] [[package]] name = "html5ever" version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c13771afe0e6e846f1e67d038d4cb29998a6779f93c809212e4e9c32efd244d4" dependencies = [ "log", "mac", "markup5ever", "proc-macro2", "quote", "syn", ] [[package]] name = "http" version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", "fnv", "itoa", ] [[package]] name = "http-body" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", "http", ] [[package]] name = "http-body-util" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", "http", "http-body", "pin-project-lite", ] [[package]] name = "httparse" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "hyper" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" dependencies = [ "atomic-waker", "bytes", "futures-channel", "futures-core", "h2", "http", "http-body", "httparse", "itoa", "pin-project-lite", "pin-utils", "smallvec", "tokio", "want", ] [[package]] name = "hyper-rustls" version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ "http", "hyper", "hyper-util", "rustls", "rustls-pki-types", "tokio", "tokio-rustls", "tower-service", ] [[package]] name = "hyper-tls" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", "hyper", "hyper-util", "native-tls", "tokio", "tokio-native-tls", "tower-service", ] [[package]] name = "hyper-util" version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52e9a2a24dc5c6821e71a7030e1e14b7b632acac55c40e9d2e082c621261bb56" dependencies = [ "base64", "bytes", "futures-channel", "futures-core", "futures-util", "http", "http-body", "hyper", "ipnet", "libc", "percent-encoding", "pin-project-lite", "socket2", "system-configuration", "tokio", "tower-service", "tracing", "windows-registry", ] [[package]] name = "icu_collections" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" dependencies = [ "displaydoc", "potential_utf", "yoke", "zerofrom", "zerovec", ] [[package]] name = "icu_locale_core" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" dependencies = [ "displaydoc", "litemap", "tinystr", "writeable", "zerovec", ] [[package]] name = "icu_normalizer" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" dependencies = [ "icu_collections", "icu_normalizer_data", "icu_properties", "icu_provider", "smallvec", "zerovec", ] [[package]] name = "icu_normalizer_data" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_properties" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99" dependencies = [ "icu_collections", "icu_locale_core", "icu_properties_data", "icu_provider", "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899" [[package]] name = "icu_provider" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" dependencies = [ "displaydoc", "icu_locale_core", "writeable", "yoke", "zerofrom", "zerotrie", "zerovec", ] [[package]] name = "idna" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" dependencies = [ "idna_adapter", "smallvec", "utf8_iter", ] [[package]] name = "idna_adapter" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ "icu_normalizer", "icu_properties", ] [[package]] name = "indexmap" version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" dependencies = [ "equivalent", "hashbrown", ] [[package]] name = "ipnet" version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "iri-string" version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397" dependencies = [ "memchr", "serde", ] [[package]] name = "is_terminal_polyfill" version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "itoa" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jiff" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49cce2b81f2098e7e3efc35bc2e0a6b7abec9d34128283d7a26fa8f32a6dbb35" dependencies = [ "jiff-static", "log", "portable-atomic", "portable-atomic-util", "serde_core", ] [[package]] name = "jiff-static" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "980af8b43c3ad5d8d349ace167ec8170839f753a42d233ba19e08afe1850fa69" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "jni" version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec" dependencies = [ "cesu8", "combine", "jni-sys", "log", "thiserror", "walkdir", ] [[package]] name = "jni-sys" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "js-sys" version = "0.3.82" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" dependencies = [ "once_cell", "wasm-bindgen", ] [[package]] name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" [[package]] name = "linux-raw-sys" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "litemap" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" [[package]] name = "lock_api" version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" dependencies = [ "scopeguard", ] [[package]] name = "log" version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" [[package]] name = "mac" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" [[package]] name = "maplit" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" [[package]] name = "markup5ever" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16ce3abbeba692c8b8441d036ef91aea6df8da2c6b6e21c7e14d3c18e526be45" dependencies = [ "log", "phf", "phf_codegen", "string_cache", "string_cache_codegen", "tendril", ] [[package]] name = "markup5ever_rcdom" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edaa21ab3701bfee5099ade5f7e1f84553fd19228cf332f13cd6e964bf59be18" dependencies = [ "html5ever", "markup5ever", "tendril", "xml5ever", ] [[package]] name = "memchr" version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "mime" version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "mime_guess" version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" dependencies = [ "mime", "unicase", ] [[package]] name = "mio" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" dependencies = [ "libc", "wasi", "windows-sys 0.61.2", ] [[package]] name = "native-tls" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" dependencies = [ "libc", "log", "openssl", "openssl-probe", "openssl-sys", "schannel", "security-framework", "security-framework-sys", "tempfile", ] [[package]] name = "new_debug_unreachable" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" [[package]] name = "once_cell" version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "once_cell_polyfill" version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "openssl" version = "0.10.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" dependencies = [ "bitflags", "cfg-if", "foreign-types", "libc", "once_cell", "openssl-macros", "openssl-sys", ] [[package]] name = "openssl-macros" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "openssl-probe" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" version = "0.9.111" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" dependencies = [ "cc", "libc", "pkg-config", "vcpkg", ] [[package]] name = "parking_lot" version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" dependencies = [ "lock_api", "parking_lot_core", ] [[package]] name = "parking_lot_core" version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", "windows-link", ] [[package]] name = "percent-encoding" version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "phf" version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" dependencies = [ "phf_shared", ] [[package]] name = "phf_codegen" version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" dependencies = [ "phf_generator", "phf_shared", ] [[package]] name = "phf_generator" version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ "phf_shared", "rand", ] [[package]] name = "phf_shared" version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" dependencies = [ "siphasher", ] [[package]] name = "pin-project-lite" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "portable-atomic" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" [[package]] name = "portable-atomic-util" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" dependencies = [ "portable-atomic", ] [[package]] name = "potential_utf" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" dependencies = [ "zerovec", ] [[package]] name = "precomputed-hash" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "proc-macro2" version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ "proc-macro2", ] [[package]] name = "r-efi" version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "rand" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "rand_core", ] [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" [[package]] name = "redox_syscall" version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ "bitflags", ] [[package]] name = "regex" version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "reqwest" version = "0.12.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" dependencies = [ "base64", "bytes", "encoding_rs", "futures-channel", "futures-core", "futures-util", "h2", "http", "http-body", "http-body-util", "hyper", "hyper-rustls", "hyper-tls", "hyper-util", "js-sys", "log", "mime", "mime_guess", "native-tls", "percent-encoding", "pin-project-lite", "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", "sync_wrapper", "tokio", "tokio-native-tls", "tower", "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", ] [[package]] name = "ring" version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", "getrandom 0.2.16", "libc", "untrusted", "windows-sys 0.52.0", ] [[package]] name = "rustix" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", "windows-sys 0.61.2", ] [[package]] name = "rustls" version = "0.23.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" dependencies = [ "once_cell", "rustls-pki-types", "rustls-webpki", "subtle", "zeroize", ] [[package]] name = "rustls-pki-types" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a" dependencies = [ "zeroize", ] [[package]] name = "rustls-webpki" version = "0.103.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" dependencies = [ "ring", "rustls-pki-types", "untrusted", ] [[package]] name = "rustversion" version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "same-file" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" dependencies = [ "winapi-util", ] [[package]] name = "schannel" version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" dependencies = [ "windows-sys 0.61.2", ] [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "security-framework" version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ "bitflags", "core-foundation", "core-foundation-sys", "libc", "security-framework-sys", ] [[package]] name = "security-framework-sys" version = "2.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" dependencies = [ "core-foundation-sys", "libc", ] [[package]] name = "serde" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ "serde_core", "serde_derive", ] [[package]] name = "serde_core" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ "itoa", "memchr", "ryu", "serde", "serde_core", ] [[package]] name = "serde_urlencoded" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", "itoa", "ryu", "serde", ] [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "siphasher" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" [[package]] name = "slab" version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "smallvec" version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" dependencies = [ "libc", "windows-sys 0.60.2", ] [[package]] name = "stable_deref_trait" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "string_cache" version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" dependencies = [ "new_debug_unreachable", "parking_lot", "phf_shared", "precomputed-hash", "serde", ] [[package]] name = "string_cache_codegen" version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0" dependencies = [ "phf_generator", "phf_shared", "proc-macro2", "quote", ] [[package]] name = "strsim" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "subtle" version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" version = "2.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "sync_wrapper" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" dependencies = [ "futures-core", ] [[package]] name = "synstructure" version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "system-configuration" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ "bitflags", "core-foundation", "system-configuration-sys", ] [[package]] name = "system-configuration-sys" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" dependencies = [ "core-foundation-sys", "libc", ] [[package]] name = "tempfile" version = "3.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" dependencies = [ "fastrand", "getrandom 0.3.4", "once_cell", "rustix", "windows-sys 0.61.2", ] [[package]] name = "tendril" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" dependencies = [ "futf", "mac", "utf-8", ] [[package]] name = "thiserror" version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "tinystr" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" dependencies = [ "displaydoc", "zerovec", ] [[package]] name = "tokio" version = "1.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" dependencies = [ "bytes", "libc", "mio", "pin-project-lite", "socket2", "windows-sys 0.61.2", ] [[package]] name = "tokio-native-tls" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" dependencies = [ "native-tls", "tokio", ] [[package]] name = "tokio-rustls" version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ "rustls", "tokio", ] [[package]] name = "tokio-util" version = "0.7.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" dependencies = [ "bytes", "futures-core", "futures-sink", "pin-project-lite", "tokio", ] [[package]] name = "tower" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" dependencies = [ "futures-core", "futures-util", "pin-project-lite", "sync_wrapper", "tokio", "tower-layer", "tower-service", ] [[package]] name = "tower-http" version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" dependencies = [ "bitflags", "bytes", "futures-util", "http", "http-body", "iri-string", "pin-project-lite", "tower", "tower-layer", "tower-service", ] [[package]] name = "tower-layer" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-service" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "pin-project-lite", "tracing-core", ] [[package]] name = "tracing-core" version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", ] [[package]] name = "try-lock" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "unicase" version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" [[package]] name = "unicode-ident" version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "untrusted" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" version = "2.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" dependencies = [ "form_urlencoded", "idna", "percent-encoding", "serde", ] [[package]] name = "utf-8" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] name = "utf8_iter" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "utf8parse" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "vcpkg" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "wadl" version = "0.5.3" dependencies = [ "async-trait", "clap", "env_logger", "form_urlencoded", "html2md", "iri-string", "lazy_static", "log", "maplit", "mime", "proc-macro2", "quote", "reqwest", "serde_json", "syn", "url", "xmltree", ] [[package]] name = "walkdir" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", ] [[package]] name = "want" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ "try-lock", ] [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" version = "1.0.1+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" dependencies = [ "wit-bindgen", ] [[package]] name = "wasm-bindgen" version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" version = "0.4.55" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "551f88106c6d5e7ccc7cd9a16f312dd3b5d36ea8b4954304657d5dfba115d4a0" dependencies = [ "cfg-if", "js-sys", "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" dependencies = [ "bumpalo", "proc-macro2", "quote", "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" dependencies = [ "unicode-ident", ] [[package]] name = "web-sys" version = "0.3.82" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] name = "winapi-util" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ "windows-sys 0.61.2", ] [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-registry" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" dependencies = [ "windows-link", "windows-result", "windows-strings", ] [[package]] name = "windows-result" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ "windows-link", ] [[package]] name = "windows-strings" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ "windows-link", ] [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets 0.52.6", ] [[package]] name = "windows-sys" version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ "windows-targets 0.53.5", ] [[package]] name = "windows-sys" version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ "windows-link", ] [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] [[package]] name = "windows-targets" version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ "windows-link", "windows_aarch64_gnullvm 0.53.1", "windows_aarch64_msvc 0.53.1", "windows_i686_gnu 0.53.1", "windows_i686_gnullvm 0.53.1", "windows_i686_msvc 0.53.1", "windows_x86_64_gnu 0.53.1", "windows_x86_64_gnullvm 0.53.1", "windows_x86_64_msvc 0.53.1", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_gnullvm" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_aarch64_msvc" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnu" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_gnullvm" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_i686_msvc" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnu" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_gnullvm" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "windows_x86_64_msvc" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "wit-bindgen" version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "writeable" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" [[package]] name = "xml" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "838dd679b10a4180431ce7c2caa6e5585a7c8f63154c19ae99345126572e80cc" [[package]] name = "xml5ever" version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bbb26405d8e919bc1547a5aa9abc95cbfa438f04844f5fdd9dc7596b748bf69" dependencies = [ "log", "mac", "markup5ever", ] [[package]] name = "xmltree" version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cbc04313cab124e498ab1724e739720807b6dc405b9ed0edc5860164d2e4ff70" dependencies = [ "xml", ] [[package]] name = "yoke" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" dependencies = [ "stable_deref_trait", "yoke-derive", "zerofrom", ] [[package]] name = "yoke-derive" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", "syn", "synstructure", ] [[package]] name = "zerofrom" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", "syn", "synstructure", ] [[package]] name = "zeroize" version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" [[package]] name = "zerotrie" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" dependencies = [ "displaydoc", "yoke", "zerofrom", ] [[package]] name = "zerovec" version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" dependencies = [ "yoke", "zerofrom", "zerovec-derive", ] [[package]] name = "zerovec-derive" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", "syn", ] wadl-0.5.3/Cargo.toml0000644000000047410000000000100077740ustar # 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 = "wadl" version = "0.5.3" authors = ["Jelmer Vernooij "] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false default-run = "wadlc" description = "A WADL parser for Rust" readme = "README.md" license = "Apache-2.0" repository = "https://github.com/jelmer/wadl" [features] async = ["dep:async-trait"] blocking = ["reqwest/blocking"] cli = [ "dep:clap", "dep:env_logger", "codegen", ] codegen = [ "dep:proc-macro2", "dep:quote", "dep:syn", "dep:html2md", ] default = [ "cli", "blocking", ] [lib] name = "wadl" path = "src/lib.rs" [[bin]] name = "wadl-ast" path = "src/bin/wadl-ast.rs" required-features = ["cli"] [[bin]] name = "wadlc" path = "src/bin/wadlc.rs" required-features = ["cli"] [[test]] name = "ast_tests" path = "tests/ast_tests.rs" [[test]] name = "codegen_tests" path = "tests/codegen_tests.rs" [[test]] name = "lib_tests" path = "tests/lib_tests.rs" [[test]] name = "parse_tests" path = "tests/parse_tests.rs" [[test]] name = "parsing_tests" path = "tests/parsing_tests.rs" [dependencies.async-trait] version = "0.1" optional = true [dependencies.clap] version = "4" features = [ "derive", "env", ] optional = true [dependencies.env_logger] version = ">=0.10" optional = true [dependencies.form_urlencoded] version = "1.2.1" [dependencies.html2md] version = "0.2.15" optional = true [dependencies.iri-string] version = ">=0.7" features = ["std"] [dependencies.lazy_static] version = "1" [dependencies.log] version = "0.4.27" [dependencies.mime] version = "0.3.17" [dependencies.proc-macro2] version = "1" optional = true [dependencies.quote] version = "1" optional = true [dependencies.reqwest] version = ">=0.11" features = [ "json", "multipart", ] [dependencies.serde_json] version = "1.0.145" [dependencies.syn] version = "2" optional = true [dependencies.url] version = "2" [dependencies.xmltree] version = ">=0.10.0" [dev-dependencies.maplit] version = "1.0.2" wadl-0.5.3/Cargo.toml.orig0000644000000024020000000000100107230ustar [package] name = "wadl" version = "0.5.3" edition = "2021" license = "Apache-2.0" description = "A WADL parser for Rust" repository = "https://github.com/jelmer/wadl" authors = ["Jelmer Vernooij "] default-run = "wadlc" [dependencies] clap = { version = "4", features = ["derive", "env"], optional = true } env_logger = { version = ">=0.10", optional = true } form_urlencoded = "1.2.1" html2md = { version = "0.2.15", optional = true } lazy_static = "1" log = "0.4.27" mime = "0.3.17" proc-macro2 = { version = "1", optional = true } quote = { version = "1", optional = true } reqwest = { version = ">=0.11", features = ["json", "multipart"] } serde_json = "1.0.145" syn = { version = "2", optional = true } url = "2" xmltree = ">=0.10.0" iri-string = { version = ">=0.7", features = ["std"] } async-trait = { version = "0.1", optional = true } [features] default = ["cli", "blocking"] codegen = ["dep:proc-macro2", "dep:quote", "dep:syn", "dep:html2md"] cli = ["dep:clap", "dep:env_logger", "codegen"] async = ["dep:async-trait"] blocking = ["reqwest/blocking"] [[bin]] name = "wadlc" path = "src/bin/wadlc.rs" required-features = ["cli"] [[bin]] name = "wadl-ast" path = "src/bin/wadl-ast.rs" required-features = ["cli"] [dev-dependencies] maplit = "1.0.2" wadl-0.5.3/Cargo.toml.orig000064400000000000000000000024021046102023000134450ustar 00000000000000[package] name = "wadl" version = "0.5.3" edition = "2021" license = "Apache-2.0" description = "A WADL parser for Rust" repository = "https://github.com/jelmer/wadl" authors = ["Jelmer Vernooij "] default-run = "wadlc" [dependencies] clap = { version = "4", features = ["derive", "env"], optional = true } env_logger = { version = ">=0.10", optional = true } form_urlencoded = "1.2.1" html2md = { version = "0.2.15", optional = true } lazy_static = "1" log = "0.4.27" mime = "0.3.17" proc-macro2 = { version = "1", optional = true } quote = { version = "1", optional = true } reqwest = { version = ">=0.11", features = ["json", "multipart"] } serde_json = "1.0.145" syn = { version = "2", optional = true } url = "2" xmltree = ">=0.10.0" iri-string = { version = ">=0.7", features = ["std"] } async-trait = { version = "0.1", optional = true } [features] default = ["cli", "blocking"] codegen = ["dep:proc-macro2", "dep:quote", "dep:syn", "dep:html2md"] cli = ["dep:clap", "dep:env_logger", "codegen"] async = ["dep:async-trait"] blocking = ["reqwest/blocking"] [[bin]] name = "wadlc" path = "src/bin/wadlc.rs" required-features = ["cli"] [[bin]] name = "wadl-ast" path = "src/bin/wadl-ast.rs" required-features = ["cli"] [dev-dependencies] maplit = "1.0.2" wadl-0.5.3/README.md000064400000000000000000000024531046102023000120430ustar 00000000000000This crate contains a parser for the [Web Application Description Language (WADL)](https://www.w3.org/submissions/wadl/). It can also generate basic rust bindings based on WADL files, if the ``codegen`` feature is enabled. ## Example usage ### Simply parsing the ast ```rust let app: wadl::ast::Application = wadl::parse_file("1.0.wadl").unwrap(); println!("{:#}", app); ``` ### Generating code Create a build.rs that generates rust code: ```rust fn main() { let config = wadl::codegen::Config { // Set extra options here to influence code generation, // e.g. to rename functions. ..Default::default() }; let wadl = std::fs::read_to_string( concat!(env!("CARGO_MANIFEST_DIR"), "/x.wadl")).unwrap(); let wadl_app = wadl::parse_string(wadl.as_str()).unwrap(); let code = wadl::codegen::generate(&wadl_app, &config); let target_dir = std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap()) .canonicalize() .unwrap(); let generated = target_dir.join("generated"); std::fs::create_dir_all(&generated).unwrap(); let path = generated.join("x.wadl"); std::fs::write(path, code).unwrap(); } ``` Then, you can include the generated code from your rust code: ```rust include!(concat!(env!("OUT_DIR"), "/generated/x.rs")); ``` wadl-0.5.3/disperse.toml000064400000000000000000000001341046102023000132710ustar 00000000000000tag-name = "v$VERSION" release-timeout = 5 [github] url = "https://github.com/jelmer/wadl" wadl-0.5.3/src/ast.rs000064400000000000000000000445661046102023000125230ustar 00000000000000//! Abstract syntax tree for WADL documents. use iri_string::spec::IriSpec; use iri_string::types::RiReferenceString; use std::collections::BTreeMap; use url::Url; /// Identifier for a resource, method, parameter, etc. pub type Id = String; /// Parameter style #[derive(Debug, PartialEq, Eq, Clone)] pub enum ParamStyle { /// Specifies a component of the representation formatted as a string encoding of the parameter value according to the rules of the media type. Plain, /// Specifies a matrix URI component. Matrix, /// Specifies a URI query parameter represented according to the rules for the query component media type specified by the queryType attribute. Query, /// Specifies a HTTP header that pertains to the HTTP request (resource or request) or HTTP response (response) Header, /// The parameter is represented as a string encoding of the parameter value and is substituted into the value of the path attribute of the resource element as described in section 2.6.1. Template, } /// A WADL application. #[derive(Debug)] pub struct Application { /// Resources defined at the application level. pub resources: Vec, /// Resource types defined at the application level. pub resource_types: Vec, /// Documentation for the application. pub docs: Vec, /// List of grammars pub grammars: Vec, /// Representations defined at the application level. pub representations: Vec, } impl Application { /// Get a resource type by its ID. pub fn get_resource_type_by_id(&self, id: &str) -> Option<&ResourceType> { self.resource_types.iter().find(|rt| id == rt.id.as_str()) } /// Get a resource type by its href, which may be a fragment or a full URL. pub fn get_resource_type_by_href(&self, href: &Url) -> Option<&ResourceType> { // TODO(jelmer): Check that href matches us? if let Some(fragment) = href.fragment() { self.get_resource_type_by_id(fragment) } else { None } } /// Iterate over all resources defined in this application. pub fn iter_resources(&self) -> impl Iterator { self.resources .iter() .flat_map(|rs| rs.resources.iter().map(|r| (r.url(rs.base.as_ref()), r))) } /// Get a resource by its ID. pub fn get_resource_by_href(&self, href: &Url) -> Option<&Resource> { self.iter_resources() .find(|(url, _)| url == href) .map(|(_, r)| r) } /// Iterate over all types defined in this application. pub fn iter_referenced_types(&self) -> impl Iterator + '_ { self.iter_resources() .flat_map(|(_u, r)| r.iter_referenced_types().map(|s| s.to_string())) .chain( self.resource_types .iter() .flat_map(|rt| rt.iter_referenced_types().map(|s| s.to_string())), ) } /// Iterate over all parameters defined in this application. pub fn iter_all_params(&self) -> impl Iterator { self.iter_resources() .flat_map(|(_u, r)| r.iter_all_params()) .chain( self.resource_types .iter() .flat_map(|rt| rt.iter_all_params()), ) .chain( self.representations .iter() .flat_map(|r| r.iter_all_params()), ) } } impl std::str::FromStr for Application { type Err = crate::parse::Error; fn from_str(s: &str) -> Result { crate::parse::parse_string(s) } } #[derive(Debug)] /// A collection of resources. pub struct Resources { /// The base URL for the resources. pub base: Option, /// The resources defined at this level. pub resources: Vec, } #[derive(Debug)] /// A grammar pub struct Grammar { /// The href of the grammar. pub href: RiReferenceString, } #[derive(Debug, Clone, PartialEq, Eq)] /// A reference to a resource type. pub enum ResourceTypeRef { /// A reference to a resource type defined in the same document. Id(Id), /// A reference to a resource type defined in another document. Link(Url), /// An empty reference. Empty, } impl std::str::FromStr for ResourceTypeRef { type Err = String; fn from_str(s: &str) -> Result { match s { "" => Ok(ResourceTypeRef::Empty), s => { if let Some(s) = s.strip_prefix('#') { Ok(ResourceTypeRef::Id(s.to_string())) } else { Ok(ResourceTypeRef::Link( s.parse().map_err(|e| format!("{}", e))?, )) } } } } } #[test] fn parse_resource_type_ref() { use crate::ast::ResourceTypeRef::*; use std::str::FromStr; assert_eq!(Empty, ResourceTypeRef::from_str("").unwrap()); assert_eq!( Id("id".to_owned()), ResourceTypeRef::from_str("#id").unwrap() ); assert_eq!( Link(Url::parse("https://example.com").unwrap()), ResourceTypeRef::from_str("https://example.com").unwrap() ); } impl ResourceTypeRef { /// Return the ID of the resource type reference. pub fn id(&self) -> Option<&str> { match self { ResourceTypeRef::Id(id) => Some(id), ResourceTypeRef::Link(l) => l.fragment(), ResourceTypeRef::Empty => None, } } } #[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] /// An option element defines one of a set of possible values for the parameter represented by its parent param element. pub struct Options(BTreeMap>); impl Options { /// Create a new options object pub fn new() -> Self { Self::default() } /// Number of items in this Options pub fn len(&self) -> usize { self.0.len() } /// Iterate over all items in this Options pub fn iter(&self) -> impl Iterator)> { self.0.iter().map(|(k, v)| (k.as_str(), v.as_ref())) } /// Return an iterator over all keys pub fn keys(&self) -> impl Iterator { self.0.keys().map(|k| k.as_str()) } /// Check if this Options is empty pub fn is_empty(&self) -> bool { self.0.is_empty() } /// Insert a new key-value pair into this Options pub fn insert(&mut self, key: String, value: Option) { self.0.insert(key, value); } /// Get the value for a key pub fn get(&self, key: &str) -> Option<&Option> { self.0.get(key) } } impl From> for Options { fn from(v: Vec) -> Self { Self(v.into_iter().map(|s| (s, None)).collect()) } } impl From> for Options { fn from(v: Vec<&str>) -> Self { Self(v.into_iter().map(|s| (s.to_string(), None)).collect()) } } #[derive(Debug, Clone)] /// A resource pub struct Resource { /// The ID of the resource. pub id: Option, /// The path of the resource. pub path: Option, /// The types of the resource. pub r#type: Vec, /// The query type of the resource. pub query_type: mime::Mime, /// The methods defined at this level. pub methods: Vec, /// The docs for the resource. pub docs: Vec, /// Sub-resources of this resource. pub subresources: Vec, /// The params for this resource. pub params: Vec, } impl Resource { /// Get the URL of this resource. pub fn url(&self, base_url: Option<&Url>) -> Url { if let Some(base_url) = base_url { base_url.join(self.path.as_ref().unwrap()).unwrap() } else { Url::parse(self.path.as_ref().unwrap()).unwrap() } } /// Iterate over all parameters defined in this resource. pub(crate) fn iter_all_params(&self) -> Box + '_> { Box::new( self.params .iter() .chain(self.subresources.iter().flat_map(|r| r.iter_all_params())) .chain(self.methods.iter().flat_map(|m| m.iter_all_params())), ) } /// Iterate over all types referenced by this resource. pub fn iter_referenced_types(&self) -> impl Iterator + '_ { self.iter_all_params().map(|p| p.r#type.as_str()) } } #[test] fn test_resource_url() { let r = Resource { id: None, path: Some("/foo".to_string()), r#type: vec![], query_type: mime::APPLICATION_JSON, methods: vec![], docs: vec![], subresources: vec![], params: vec![], }; assert_eq!( r.url(Some(&Url::parse("http://example.com").unwrap())), Url::parse("http://example.com/foo").unwrap() ); assert_eq!( r.url(Some(&Url::parse("http://example.com/bar").unwrap())), Url::parse("http://example.com/foo").unwrap() ); } #[derive(Debug, Clone)] /// A HTTP Method pub struct Method { /// Identifier of this method pub id: Id, /// The name of the method. pub name: String, /// The docs for the method. pub docs: Vec, /// The request for the method. pub request: Request, /// The responses for the method. pub responses: Vec, } impl Method { fn iter_all_params(&self) -> impl Iterator { self.request .iter_all_params() .chain(self.responses.iter().flat_map(|r| r.iter_all_params())) } } #[derive(Debug, Clone, PartialEq, Eq, Default)] /// Documentation pub struct Doc { /// The title of the documentation. pub title: Option, /// The language of the documentation. pub lang: Option, /// The content of the documentation. pub content: String, /// The namespace of the documentation. pub xmlns: Option, } impl Doc { /// Create a new documentation object. pub fn new(content: String) -> Self { Self { content, ..Default::default() } } } #[derive(Debug, Clone, PartialEq, Eq)] /// A link to another resource. pub struct Link { /// The resource type of the link. pub resource_type: Option, /// Optional token that identifies the relationship of the resource identified by the link to /// the resource whose representation the link is embedded in. The value is scoped by the value /// of the ancestor representation element's profile attribute. pub relation: Option, /// An optional token that identifies the relationship of the resource whose representation /// the link is embedded in to the resource identified by the link. This is the reverse /// relationship to that identified by the rel attribute. The value is scoped by the value /// of the ancestor representation element's profile attribute. pub reverse_relation: Option, /// Optional documentation pub doc: Option, } #[derive(Debug, Clone, PartialEq, Eq)] /// A parameter pub struct Param { /// The style of the parameter. pub style: ParamStyle, /// The ID of the parameter. pub id: Option, /// The name of the parameter. pub name: String, /// The type of the parameter. This will be a XSD type, e.g. `xs:string`. pub r#type: String, /// Path of the parameter. pub path: Option, /// Whether the parameter is required, i.e. must be present in the request. pub required: bool, /// Whether the parameter is repeating. pub repeating: bool, /// The fixed value of the parameter. pub fixed: Option, /// The documentation for the parameter. pub doc: Option, /// The links for the parameter. pub links: Vec, /// The options for the parameter. pub options: Option, } #[derive(Debug, Clone, PartialEq, Eq, Default)] /// A representation definition pub struct RepresentationDef { /// The ID of the representation. pub id: Option, /// The media type of the representation. pub media_type: Option, /// The element of the representation. pub element: Option, /// The profile of the representation. pub profile: Option, /// The documentation for the representation. pub docs: Vec, /// The parameters for the representation. pub params: Vec, } impl RepresentationDef { fn iter_all_params(&self) -> impl Iterator { self.params.iter() } } #[derive(Debug, Clone)] /// A reference to a representation. pub enum RepresentationRef { /// A reference to a representation defined in the same document. Id(Id), /// A reference to a representation defined in another document. Link(Url), } impl RepresentationRef { /// Return the ID of the representation reference. pub fn id(&self) -> Option<&str> { match self { RepresentationRef::Id(id) => Some(id), RepresentationRef::Link(l) => l.fragment(), } } } #[derive(Debug, Clone)] /// A representation pub enum Representation { /// A reference to a representation defined in the same document. Reference(RepresentationRef), /// A definition of a representation. Definition(RepresentationDef), } impl Representation { /// Return the content type of this representation. pub fn media_type(&self) -> Option<&mime::Mime> { match self { Representation::Reference(_) => None, Representation::Definition(d) => d.media_type.as_ref(), } } /// Return the URL of this representation. pub fn url(&self, base_url: &Url) -> Option { match self { Representation::Reference(RepresentationRef::Id(id)) => { let mut url = base_url.clone(); url.set_fragment(Some(id)); Some(url) } Representation::Reference(RepresentationRef::Link(l)) => Some(l.clone()), Representation::Definition(d) => d.url(base_url), } } /// Return the definition of this representation. pub fn as_def(&self) -> Option<&RepresentationDef> { match self { Representation::Reference(_) => None, Representation::Definition(d) => Some(d), } } /// Iterate over all parameters defined in this representation. pub fn iter_all_params(&self) -> impl Iterator { // TODO: Make this into a proper iterator let params = match self { Representation::Reference(_) => vec![], Representation::Definition(d) => d.iter_all_params().collect::>(), }; params.into_iter() } } #[test] fn test_representation_url() { let base_url = Url::parse("http://example.com").unwrap(); let r = Representation::Reference(RepresentationRef::Id("foo".to_string())); assert_eq!( r.url(&base_url).unwrap(), Url::parse("http://example.com#foo").unwrap() ); let r = Representation::Reference(RepresentationRef::Link( Url::parse("http://example.com#foo").unwrap(), )); assert_eq!( r.url(&base_url).unwrap(), Url::parse("http://example.com#foo").unwrap() ); let r = Representation::Definition(RepresentationDef { id: Some("foo".to_string()), ..Default::default() }); assert_eq!( r.url(&base_url).unwrap(), Url::parse("http://example.com#foo").unwrap() ); } #[test] fn test_representation_id() { let r = Representation::Reference(RepresentationRef::Id("foo".to_string())); assert_eq!(r.as_def(), None); let r = Representation::Definition(RepresentationDef { id: Some("foo".to_string()), ..Default::default() }); assert_eq!(r.as_def().unwrap().id, Some("foo".to_string())); } impl RepresentationDef { /// Fully qualify the URL of this representation. pub fn url(&self, base_url: &Url) -> Option { if let Some(id) = &self.id { let mut url = base_url.clone(); url.set_fragment(Some(id)); Some(url) } else { None } } } #[derive(Debug, Default, Clone)] /// A request pub struct Request { /// The docs for the request. pub docs: Vec, /// The parameters for the request. pub params: Vec, /// The representations for the request. pub representations: Vec, } impl Request { fn iter_all_params(&self) -> impl Iterator { self.params.iter().chain( self.representations .iter() .filter_map(|r| r.as_def().map(|r| r.iter_all_params())) .flatten(), ) } } #[derive(Debug, Clone, Default)] /// A response pub struct Response { /// The docs for the response. pub docs: Vec, /// The parameters for the response. pub params: Vec, /// The status of the response. pub status: Option, /// The representations for the response. pub representations: Vec, } impl Response { fn iter_all_params(&self) -> impl Iterator { self.params.iter().chain( self.representations .iter() .filter_map(|r| r.as_def().map(|r| r.iter_all_params())) .flatten(), ) } } #[derive(Debug)] /// A resource type pub struct ResourceType { /// The ID of the resource type. pub id: Id, /// The query type of the resource type. pub query_type: mime::Mime, /// The methods defined at this level. pub methods: Vec, /// The docs for the resource type. pub docs: Vec, /// The subresources of the resource type. pub subresources: Vec, /// The params for the resource type. pub params: Vec, } impl ResourceType { /// Iterate over all parameters defined in this resource type. pub(crate) fn iter_all_params(&self) -> impl Iterator { self.params .iter() .chain(self.methods.iter().flat_map(|m| m.iter_all_params())) } /// Returns an iterator over all types referenced by this resource type. pub fn iter_referenced_types(&self) -> impl Iterator + '_ { self.iter_all_params().map(|p| p.r#type.as_str()) } } wadl-0.5.3/src/codegen.rs000064400000000000000000002273171046102023000133350ustar 00000000000000//! Generate Rust code from WADL files use crate::ast::*; use std::collections::HashMap; /// MIME type for XHTML pub const XHTML_MIME_TYPE: &str = "application/xhtml+xml"; #[allow(missing_docs)] pub enum ParamContainer<'a> { Request(&'a Method, &'a Request), Response(&'a Method, &'a Response), Representation(&'a RepresentationDef), } /// Convert wadl names (with dashes) to camel-case Rust names pub fn camel_case_name(name: &str) -> String { let mut it = name.chars().peekable(); let mut result = String::new(); // Uppercase the first letter if let Some(c) = it.next() { result.push_str(&c.to_uppercase().collect::()); } while it.peek().is_some() { let c = it.next().unwrap(); if c == '_' || c == '-' { if let Some(next) = it.next() { result.push_str(&next.to_uppercase().collect::()); } } else { result.push(c); } } result } /// Convert wadl names (with dashes) to snake-case Rust names pub fn snake_case_name(name: &str) -> String { let mut name = name.to_string(); name = name.replace('-', "_"); let mut result = String::new(); let mut prev_upper = false; for c in name.chars() { if c.is_uppercase() { if !result.is_empty() && !prev_upper && !result.ends_with('_') { result.push('_'); } result.push_str(&c.to_lowercase().to_string()); prev_upper = true; } else { result.push(c); prev_upper = false; } } result } fn strip_code_examples(input: String) -> String { let mut in_example = false; input .lines() .filter(|line| { if !in_example && (line.starts_with("```python") || *line == "```") { in_example = true; false } else if line.starts_with("```") { in_example = false; false } else { !in_example } }) .collect::>() .join("\n") } /// Format the given `Doc` object into a string. /// /// # Arguments /// * `input` - The `Doc` object to format. /// * `config` - The configuration to use. /// /// # Returns /// The formatted string. fn format_doc(input: &Doc, config: &Config) -> String { match input.xmlns.as_ref().map(|x| x.as_str()) { Some("http://www.w3.org/1999/xhtml") => { let mut text = html2md::parse_html(&input.content); if config.strip_code_examples { text = strip_code_examples(text); } text.lines().collect::>().join("\n") } Some(xmlns) => { log::warn!("Unknown xmlns: {}", xmlns); input.content.lines().collect::>().join("\n") } None => input.content.lines().collect::>().join("\n"), } } /// Generate a docstring from the given `Doc` object. /// /// # Arguments /// * `input` - The `Doc` object to generate the docstring from. /// * `indent` - The indentation level to use. /// * `config` - The configuration to use. /// /// # Returns /// A vector of strings, each representing a line of the docstring. pub fn generate_doc(input: &Doc, indent: usize, config: &Config) -> Vec { let mut lines: Vec = vec![]; if let Some(title) = input.title.as_ref() { lines.extend(vec![format!("/// # {}\n", title), "///\n".to_string()]); } let mut text = format_doc(input, config); if let Some(reformat_docstring) = config.reformat_docstring.as_ref() { text = reformat_docstring(&text); } lines.extend( text.lines() .map(|line| format!("///{}{}\n", if line.is_empty() { "" } else { " " }, line)), ); lines .into_iter() .map(|line| format!("{:indent$}{}", "", line, indent = indent * 4)) .collect() } fn generate_resource_type_ref_accessors( field_name: &str, input: &ResourceTypeRef, param: &Param, config: &Config, ) -> Vec { let mut lines = vec![]; if let Some(id) = input.id() { let deprecated = config .deprecated_param .as_ref() .map(|x| x(param)) .unwrap_or(false); if let Some(doc) = param.doc.as_ref() { lines.extend(generate_doc(doc, 1, config)); } else { // Generate default getter documentation lines.push(format!(" /// Get the {} value.\n", param.name)); } let field_type = camel_case_name(id); let mut ret_type = field_type.to_string(); let map_fn = if let Some((map_type, map_fn)) = config .map_type_for_accessor .as_ref() .and_then(|x| x(field_type.as_str())) { ret_type = map_type; Some(map_fn) } else { None }; if config.nillable(param) { ret_type = format!("Option<{}>", ret_type); } let accessor_name = if let Some(rename_fn) = config.param_accessor_rename.as_ref() { rename_fn(param.name.as_str(), ret_type.as_str()) } else { None } .unwrap_or_else(|| field_name.to_string()); let visibility = config .accessor_visibility .as_ref() .and_then(|x| x(accessor_name.as_str(), field_type.as_str())) .unwrap_or_else(|| "pub".to_string()); if deprecated { lines.push(" #[deprecated]".to_string()); } lines.push(format!( " {}fn {}(&self) -> {} {{\n", if visibility.is_empty() { "".to_string() } else { format!("{} ", visibility) }, accessor_name, ret_type )); if !config.nillable(param) { if let Some(map_fn) = map_fn { lines.push(format!( " {}({}(self.{}.clone())\n", map_fn, field_type, field_name )); } else { lines.push(format!( " {}(self.{}.clone())\n", field_type, field_name )); } } else { lines.push(format!( " self.{}.as_ref().map(|x| {}(x.clone())){}\n", field_name, field_type, if let Some(map_fn) = map_fn { format!(".map({})", map_fn) } else { "".to_string() } )); } lines.push(" }\n".to_string()); lines.push("\n".to_string()); if deprecated { lines.push(" #[deprecated]".to_string()); } // Add setter documentation lines.push(format!(" /// Set the {} value.\n", param.name)); lines.push(format!( " {}fn set_{}(&mut self, value: {}) {{\n", if visibility.is_empty() { "".to_string() } else { format!("{} ", visibility) }, accessor_name, ret_type )); if !config.nillable(param) { lines.push(format!( " self.{} = value.url().clone();\n", field_name )); } else { lines.push(format!( " self.{} = value.map(|x| x.url().clone());\n", field_name )); } lines.push(" }\n".to_string()); if let Some(extend_accessor) = config.extend_accessor.as_ref() { lines.extend(extend_accessor( param, accessor_name.as_str(), ret_type.as_str(), config, )); } } lines } fn generate_representation( input: &RepresentationDef, config: &Config, options_names: &HashMap, ) -> Vec { let mut lines = vec![]; if input.media_type == Some(mime::APPLICATION_JSON) { lines.extend(generate_representation_struct_json( input, config, options_names, )); } else { panic!("Unknown media type: {:?}", input.media_type); } let name = input.id.as_ref().unwrap().as_str(); let name = camel_case_name(name); lines.push(format!("impl {} {{\n", name)); for param in &input.params { // Check if this param was filtered out if let Some(filter) = config.filter_param.as_ref() { if !filter(param) { continue; } } let field_name = snake_case_name(param.name.as_str()); // We expect to support multiple types here in the future for link in ¶m.links { if let Some(r) = link.resource_type.as_ref() { lines.extend(generate_resource_type_ref_accessors( &field_name, r, param, config, )); } } } lines.push("}\n".to_string()); lines.push("\n".to_string()); if let Some(generate) = config.generate_representation_traits.as_ref() { lines.extend(generate(input, name.as_str(), input, config).unwrap_or(vec![])); } lines } /// Generate the Rust type for a representation fn resource_type_rust_type(r: &ResourceTypeRef) -> String { if let Some(id) = r.id() { camel_case_name(id) } else { "url::Url".to_string() } } fn simple_type_rust_type( container: &ParamContainer, type_name: &str, param: &Param, config: &Config, ) -> (String, Vec) { let tn = if let Some(override_name) = config.override_type_name.as_ref() { override_name(container, type_name, param.name.as_str(), config) } else { None }; if let Some(tn) = tn { return (tn, vec![]); } match type_name.split_once(':').map_or(type_name, |(_, n)| n) { "date" => ("chrono::NaiveDate".to_string(), vec![]), "dateTime" => ("chrono::DateTime".to_string(), vec![]), "time" => ("(chrono::Time".to_string(), vec![]), "int" => ("i32".to_string(), vec![]), "string" => ("String".to_string(), vec![]), "binary" => ("Vec".to_string(), vec![]), "boolean" => ("bool".to_string(), vec![]), u => panic!("Unknown type: {}", u), } } fn param_rust_type( container: &ParamContainer, param: &Param, config: &Config, resource_type_rust_type: impl Fn(&ResourceTypeRef) -> String, options_names: &HashMap, ) -> (String, Vec) { let (mut param_type, annotations) = if !param.links.is_empty() { if let Some(rt) = param.links[0].resource_type.as_ref() { let name = resource_type_rust_type(rt); if let Some(override_type_name) = config .override_type_name .as_ref() .and_then(|x| x(container, name.as_str(), param.name.as_str(), config)) { (override_type_name, vec![]) } else { (name, vec![]) } } else { ("url::Url".to_string(), vec![]) } } else if let Some(os) = param.options.as_ref() { let options_name = options_names.get(os).unwrap_or_else(|| { panic!("Unknown options {:?} for {}", os, param.name); }); (options_name.clone(), vec![]) } else { simple_type_rust_type(container, param.r#type.as_str(), param, config) }; if param.repeating { param_type = format!("Vec<{}>", param_type); } if config.nillable(param) { param_type = format!("Option<{}>", param_type); } (param_type, annotations) } fn readonly_rust_type(name: &str) -> String { if name.starts_with("Option<") && name.ends_with('>') { return format!( "Option<{}>", readonly_rust_type(name[7..name.len() - 1].trim()) ); } match name { "String" => "&str".to_string(), x if x.starts_with("Vec<") && x.ends_with('>') => { format!("&[{}]", x[4..x.len() - 1].trim()) } x if x.starts_with('*') => x[1..].to_string(), x => format!("&{}", x), } } fn representation_rust_type(r: &RepresentationRef) -> String { if let Some(id) = r.id() { camel_case_name(id) } else { "serde_json::Value".to_string() } } fn escape_rust_reserved(name: &str) -> &str { match name { "type" => "r#type", "match" => "r#match", "move" => "r#move", "use" => "r#use", "loop" => "r#loop", "continue" => "r#continue", "break" => "r#break", "fn" => "r#fn", "struct" => "r#struct", "enum" => "r#enum", "trait" => "r#trait", "impl" => "r#impl", "pub" => "r#pub", "as" => "r#as", "const" => "r#const", "let" => "r#let", name => name, } } fn generate_representation_struct_json( input: &RepresentationDef, config: &Config, options_names: &HashMap, ) -> Vec { let mut lines: Vec = vec![]; let name = input.id.as_ref().unwrap().as_str(); let name = camel_case_name(name); let container = ParamContainer::Representation(input); for doc in &input.docs { lines.extend(generate_doc(doc, 0, config)); } if input.docs.is_empty() { lines.push(format!( "/// Representation of the `{}` resource\n", input.id.as_ref().unwrap() )); } let derive_default = input.params.iter().all(|x| config.nillable(x)); lines.push( "#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]\n".to_string(), ); let visibility = config .representation_visibility .as_ref() .and_then(|x| x(name.as_str())) .unwrap_or_else(|| "pub".to_string()); lines.push(format!( "{}struct {} {{\n", if visibility.is_empty() { "".to_string() } else { format!("{} ", visibility) }, name )); for param in &input.params { // Check if we should filter this param if let Some(filter) = config.filter_param.as_ref() { if !filter(param) { continue; } } let param_name = snake_case_name(param.name.as_str()); let param_name = escape_rust_reserved(param_name.as_str()); let (param_type, annotations) = param_rust_type( &container, param, config, |_x| "url::Url".to_string(), options_names, ); // We provide accessors for resource types let is_pub = true; lines.push(format!(" // was: {}\n", param.r#type)); if let Some(doc) = param.doc.as_ref() { lines.extend(generate_doc(doc, 1, config)); } for ann in annotations { lines.push(format!(" {}\n", ann)); } lines.push(format!( " {}{}: {},\n", if is_pub { "pub " } else { "" }, param_name, param_type )); lines.push("\n".to_string()); } lines.push("}\n".to_string()); if derive_default { lines.push(format!("impl Default for {} {{\n", name)); lines.push(" fn default() -> Self {\n".to_string()); lines.push(" Self {\n".to_string()); for param in &input.params { // Check if this param was filtered out if let Some(filter) = config.filter_param.as_ref() { if !filter(param) { continue; } } let param_name = snake_case_name(param.name.as_str()); let param_name = escape_rust_reserved(param_name.as_str()); lines.push(format!(" {}: Default::default(),\n", param_name)); } lines.push(" }\n".to_string()); lines.push(" }\n".to_string()); lines.push("}\n".to_string()); lines.push("\n".to_string()); } lines.push("\n".to_string()); lines } fn supported_representation_def(_d: &RepresentationDef) -> bool { false } /// Generate the Rust type for a representation /// /// # Arguments /// * `input` - The representation to generate the Rust type for /// * `name` - The name of the representation /// /// # Returns /// /// The Rust type for the representation fn rust_type_for_response( method: &Method, input: &Response, name: &str, options_names: &HashMap, ) -> String { let container = ParamContainer::Response(method, input); let representations = input .representations .iter() .filter(|r| match r { Representation::Definition(ref d) => supported_representation_def(d), _ => true, }) .collect::>(); if representations.len() == 1 { assert!(input.params.is_empty()); match representations[0] { Representation::Reference(ref r) => { let id = r.id().unwrap().to_string(); camel_case_name(id.as_str()) } Representation::Definition(ref d) => { assert!(d.params.iter().all(|p| p.style == ParamStyle::Header)); let mut ret = Vec::new(); for param in &input.params { let (param_type, _annotations) = param_rust_type( &container, param, &Config::default(), resource_type_rust_type, options_names, ); ret.push(param_type); } if ret.len() == 1 { ret.into_iter().next().unwrap() } else { format!("({})", ret.join(", ")) } } } } else if representations.is_empty() { let mut ret = Vec::new(); for param in &input.params { let (param_type, _annotations) = param_rust_type( &container, param, &Config::default(), resource_type_rust_type, options_names, ); ret.push(param_type); } if ret.len() == 1 { ret.into_iter().next().unwrap() } else { format!("({})", ret.join(", ")) } } else { todo!( "multiple representations for response: {}: {:?}", name, representations ); } } fn format_arg_doc(name: &str, doc: Option<&crate::ast::Doc>, config: &Config) -> Vec { let mut lines = Vec::new(); if let Some(doc) = doc.as_ref() { let doc = format_doc(doc, config); let mut doc_lines = doc .trim_start_matches('\n') .split('\n') .collect::>() .into_iter(); lines.push(format!( " /// * `{}`: {}\n", name, doc_lines.next().unwrap().trim_end_matches(' ') )); for doc_line in doc_lines { if doc_line.is_empty() { lines.push(" ///\n".to_string()); } else { lines.push(format!(" /// {}\n", doc_line.trim_end_matches(' '))); } } } else { lines.push(format!(" /// * `{}`\n", name)); } lines } fn apply_map_fn(map_fn: Option<&str>, ret: &str, nillable: bool) -> String { if let Some(map_fn) = map_fn { if !nillable { if map_fn.starts_with('|') { format!("({})({})", map_fn, ret) } else { format!("{}({})", map_fn, ret) } } else { format!("{}.map({})", ret, map_fn) } } else { ret.to_string() } } fn serialize_representation_def( def: &RepresentationDef, config: &Config, options_names: &HashMap, ) -> Vec { let mut lines = vec![]; fn process_param( param: &Param, container: &ParamContainer, config: &Config, cb: impl Fn(&str, &str, &str) -> String, options_names: &HashMap, ) -> Vec { let param_name = escape_rust_reserved(param.name.as_str()); let (param_type, _annotations) = param_rust_type( container, param, config, resource_type_rust_type, options_names, ); let param_type = readonly_rust_type(¶m_type); let mut indent = 4; let mut lines = vec![]; let needs_iter = param_type.starts_with("Vec<") || param_type.starts_with("Option { let mp_mod = if !config.r#async { "reqwest::blocking" } else { "reqwest" }; lines.push(format!( "let mut form = {}::multipart::Form::new();\n", mp_mod )); for param in def.params.iter() { lines.extend(process_param( param, &container, config, |param_type, name, value| { format!( "form = form.part(\"{}\", {});", name, if let Some(convert_to_multipart) = config .convert_to_multipart .as_ref() .and_then(|x| x(param_type, value)) { convert_to_multipart } else { format!( "{}::multipart::Part::text({})", mp_mod, value.strip_prefix('&').unwrap_or(value) ) } ) }, options_names, )); } lines.push("req = req.multipart(form);\n".to_string()); } Some("application/x-www-form-urlencoded") => { lines.push( "let mut serializer = form_urlencoded::Serializer::new(String::new());\n" .to_string(), ); for param in def.params.iter() { lines.extend(process_param(param, &container, config, |r#type, name, value| { if r#type.contains("[") { format!("for value in {} {{ serializer.append_pair(\"{}\", &value.to_string()); }}", value.strip_prefix("&").unwrap().strip_suffix(".to_string()").unwrap(), name) } else { format!("serializer.append_pair(\"{}\", {});", name, value) } }, options_names)); } lines.push("req = req.header(reqwest::header::CONTENT_TYPE, \"application/x-www-form-urlencoded\");\n".to_string()); lines.push("req = req.body(serializer.finish());\n".to_string()); } Some("application/json") => { lines.push("let mut o = serde_json::Value::Object::new();".to_string()); for param in def.params.iter() { lines.extend(process_param( param, &container, config, |_type, name, value| format!("o.insert(\"{}\", {});", name, value), options_names, )); } lines.push("req = req.json(&o);\n".to_string()); } o => { panic!("unsupported media type {:?}", o); } } lines } /// Generate code for a WADL method (HTTP GET, POST, PUT, DELETE, etc.). /// /// This function generates Rust methods that correspond to HTTP operations defined /// in the WADL specification. It creates both representation-based methods and /// WADL-specific methods when applicable. /// /// # Arguments /// * `input` - The WADL method definition containing HTTP method details /// * `parent_id` - The ID of the parent resource type /// * `config` - Configuration options for code generation /// * `options_names` - Mapping of option sets to their generated enum names /// /// # Returns /// A vector of strings containing the generated Rust code lines fn generate_method( input: &Method, parent_id: &str, config: &Config, options_names: &HashMap, ) -> Vec { let mut lines = generate_method_representation(input, parent_id, config, options_names); for response in input.responses.iter() { if response.representations.iter().any(|r| { r.media_type().as_ref().map(|s| s.to_string()).as_deref() == Some(crate::WADL_MIME_TYPE) }) { lines.extend(generate_method_wadl(input, parent_id, config)) } } lines } /// Generate Rust code for a WADL method that retrieves WADL descriptions. /// /// This function creates methods that fetch WADL representations from endpoints. /// These methods are generated when a resource supports returning its own WADL /// description (typically for dynamic API discovery). The generated method will: /// - Build a request with appropriate WADL accept headers /// - Parse the returned WADL XML into AST structures /// - Return the corresponding resource definition /// /// # Arguments /// * `input` - The WADL method definition /// * `parent_id` - The ID of the parent resource type (used for method naming) /// * `config` - Configuration for code generation (async/sync mode) /// /// # Returns /// A vector of strings containing the generated Rust method for WADL retrieval fn generate_method_wadl(input: &Method, parent_id: &str, config: &Config) -> Vec { let mut lines = vec![]; let name = input.id.as_str(); let name = name .strip_prefix(format!("{}-", parent_id).as_str()) .unwrap_or(name); let name = snake_case_name(name); let async_prefix = if config.r#async { "async " } else { "" }; // Add documentation for WADL method lines.push(" /// Retrieve the WADL description for this resource.\n".to_string()); lines.push(" ///\n".to_string()); lines.push(" /// This method fetches the WADL (Web Application Description Language) specification\n".to_string()); lines.push( " /// for the current resource, allowing for runtime API discovery.\n".to_string(), ); lines.push(" ///\n".to_string()); lines.push(" /// # Returns\n".to_string()); lines.push(" /// Returns the `wadl::ast::Resource` definition on success, or an error if the request fails.\n".to_string()); lines.push(format!(" pub {}fn {}_wadl<'a>(&self, client: &'a dyn {}) -> std::result::Result {{\n", async_prefix, name, config.client_trait_name())); lines.push(" let mut url_ = self.url().clone();\n".to_string()); for param in input .request .params .iter() .filter(|p| p.style == ParamStyle::Query) { if let Some(fixed) = param.fixed.as_ref() { assert!(!param.repeating); lines.push(format!( " url_.query_pairs_mut().append_pair(\"{}\", \"{}\");\n", param.name, fixed )); } } lines.push("\n".to_string()); let method = input.name.as_str(); if config.r#async { lines.push(format!( " let mut req = client.request(reqwest::Method::{}, url_).await;\n", method )); } else { lines.push(format!( " let mut req = client.request(reqwest::Method::{}, url_);\n", method )); } lines.push(format!( " req = req.header(reqwest::header::ACCEPT, \"{}\");\n", crate::WADL_MIME_TYPE )); lines.push("\n".to_string()); if config.r#async { lines.push(" let wadl: wadl::ast::Application = req.send().await?.error_for_status()?.text().await?.parse()?;\n".to_string()); } else { lines.push(" let wadl: wadl::ast::Application = req.send()?.error_for_status()?.text()?.parse()?;\n".to_string()); } lines.push( " let resource = wadl.get_resource_by_href(self.url()).unwrap();\n".to_string(), ); lines.push(" Ok(resource.clone())\n".to_string()); lines.push(" }\n".to_string()); lines.push("\n".to_string()); lines } /// Generate Rust code for a WADL method that handles representations. /// /// This function creates the actual implementation of HTTP methods (GET, POST, PUT, DELETE, etc.) /// that handle request/response representations. It generates type-safe Rust methods that: /// - Accept appropriate parameters based on the WADL definition /// - Build HTTP requests with proper headers and query parameters /// - Serialize request bodies according to the representation type /// - Deserialize responses into strongly-typed Rust structures /// /// # Arguments /// * `input` - The WADL method definition containing request/response specifications /// * `parent_id` - The ID of the parent resource type (used for method naming) /// * `config` - Configuration for code generation (async/sync, visibility, etc.) /// * `options_names` - Mapping of option sets to their generated enum names /// /// # Returns /// A vector of strings containing the generated Rust method implementation fn generate_method_representation( input: &Method, parent_id: &str, config: &Config, options_names: &HashMap, ) -> Vec { let mut lines = vec![]; let name = input.id.as_str(); let name = name .strip_prefix(format!("{}-", parent_id).as_str()) .unwrap_or(name); let name = snake_case_name(name); let (ret_type, map_fn) = if input.responses.is_empty() { ("()".to_string(), None) } else { assert_eq!(1, input.responses.len(), "expected 1 response for {}", name); let mut return_type = rust_type_for_response(input, &input.responses[0], input.id.as_str(), options_names); let map_fn = if let Some((map_type, map_fn)) = config .map_type_for_response .as_ref() .and_then(|r| r(&name, &return_type, config)) { return_type = map_type; Some(map_fn) } else { None }; (return_type, map_fn) }; let visibility = config .method_visibility .as_ref() .and_then(|x| x(&name, &ret_type)) .unwrap_or("pub".to_string()); let mut line = format!( " {}{}fn {}<'a>(&self, client: &'a dyn {}", if visibility.is_empty() { "".to_string() } else { format!("{} ", visibility) }, if config.r#async { "async " } else { "" }, name, config.client_trait_name() ); let mut params = input.request.params.iter().collect::>(); params.extend( input .request .representations .iter() .filter_map(|r| match r { Representation::Definition(d) => Some(&d.params), Representation::Reference(_) => None, }) .flatten(), ); // Generate documentation for the method if input.docs.is_empty() { // Generate a default docstring based on the HTTP method and resource let method_verb = match input.name.as_str() { "GET" => "Retrieve", "POST" => "Create", "PUT" => "Update", "DELETE" => "Delete", "PATCH" => "Partially update", "HEAD" => "Get headers for", _ => "Perform operation on", }; lines.push(format!(" /// {} the resource.\n", method_verb)); lines.push(" ///\n".to_string()); } else { for doc in &input.docs { lines.extend(generate_doc(doc, 1, config)); } } if !params.is_empty() { lines.push(" /// # Arguments\n".to_string()); } for representation in &input.request.representations { match representation { Representation::Definition(_) => {} Representation::Reference(r) => { let id = camel_case_name(r.id().unwrap()); line.push_str(format!(", representation: &{}", id).as_str()); } } } let container = ParamContainer::Request(input, &input.request); for param in ¶ms { if param.fixed.is_some() { continue; } let (param_type, _annotations) = param_rust_type( &container, param, config, resource_type_rust_type, options_names, ); let param_type = readonly_rust_type(param_type.as_str()); let param_name = param.name.clone(); let param_name = escape_rust_reserved(param_name.as_str()); line.push_str(format!(", {}: {}", param_name, param_type).as_str()); lines.extend(format_arg_doc(param_name, param.doc.as_ref(), config)); } // Add returns documentation if ret_type != "()" { lines.push(" ///\n".to_string()); lines.push(" /// # Returns\n".to_string()); lines.push(format!( " /// Returns `{}` on success, or an error if the request fails.\n", ret_type )); } line.push_str(") -> std::result::Result<"); line.push_str(ret_type.as_str()); line.push_str(", wadl::Error> {\n"); lines.push(line); assert!(input .request .params .iter() .all(|p| [ParamStyle::Header, ParamStyle::Query].contains(&p.style))); lines.push(" let mut url_ = self.url().clone();\n".to_string()); for param in input .request .params .iter() .filter(|p| p.style == ParamStyle::Query) { if let Some(fixed) = param.fixed.as_ref() { assert!(!param.repeating); lines.push(format!( " url_.query_pairs_mut().append_pair(\"{}\", \"{}\");\n", param.name, fixed )); } else { let param_name = param.name.as_str(); let param_name = snake_case_name(param_name); let param_name = escape_rust_reserved(param_name.as_str()); let (param_type, _annotations) = param_rust_type( &container, param, config, resource_type_rust_type, options_names, ); let value = if !param.links.is_empty() { format!("&{}.url().to_string()", param_name) } else { format!("&{}.to_string()", param_name) }; let mut indent = 0; let needs_iter = param.repeating || param_type.starts_with("Vec<") || param_type.starts_with("Option 0 { lines.push(format!("{:indent$} }}\n", "", indent = indent)); indent -= 4; } } } lines.push("\n".to_string()); let method = input.name.as_str(); if config.r#async { lines.push(format!( " let mut req = client.request(reqwest::Method::{}, url_).await;\n", method )); } else { lines.push(format!( " let mut req = client.request(reqwest::Method::{}, url_);\n", method )); } for representation in &input.request.representations { match representation { Representation::Definition(ref d) => { lines.extend(indent( 2, serialize_representation_def(d, config, options_names).into_iter(), )); } Representation::Reference(_r) => { // TODO(jelmer): Support non-JSON representations lines.push(" req = req.json(&representation);\n".to_string()); } }; } let response_mime_types = input .responses .iter() .flat_map(|x| { x.representations.iter().filter_map(|x| match x { Representation::Definition(ref d) if supported_representation_def(d) => { d.media_type.clone() } Representation::Reference(_) => { // TODO: Look up media type of reference Some(mime::APPLICATION_JSON) } _ => None, }) }) .collect::>(); if !response_mime_types.is_empty() { lines.push(format!( " req = req.header(reqwest::header::ACCEPT, \"{}\");\n", response_mime_types .into_iter() .map(|x| x.to_string()) .collect::>() .join(", ") )); } for param in params.iter().filter(|p| p.style == ParamStyle::Header) { let value = if let Some(fixed) = param.fixed.as_ref() { format!("\"{}\"", fixed) } else { let param_name = param.name.as_str(); let param_name = snake_case_name(param_name); let param_name = escape_rust_reserved(param_name.as_str()); format!("&{}.to_string()", param_name) }; lines.push(format!( " req = req.header(\"{}\", {});\n", param.name, value )); } lines.push("\n".to_string()); if config.r#async { lines.push(" let resp = req.send().await?;\n".to_string()); } else { lines.push(" let resp = req.send()?;\n".to_string()); } lines.push(" match resp.status() {\n".to_string()); let serialize_return_types = |return_types: Vec<(String, bool)>| { if return_types.is_empty() { "Ok(())".to_string() } else if return_types.len() == 1 { format!( "Ok({})", apply_map_fn(map_fn.as_deref(), &return_types[0].0, !return_types[0].1) ) } else { let v = format!( "({})", return_types .iter() .map(|x| x.0.clone()) .collect::>() .join(", ") ); format!("Ok({})", apply_map_fn(map_fn.as_deref(), &v, false)) } }; for response in input.responses.iter() { let mut return_types = vec![]; for param in response.params.iter() { match ¶m.style { ParamStyle::Header => { if !param.links.is_empty() { let r = ¶m.links[0].resource_type.as_ref().unwrap(); if !config.nillable(param) { return_types.push(( format!( "{}(resp.headers().get(\"{}\")?.to_str()?.parse().unwrap())", resource_type_rust_type(r), param.name ), true, )); } else { return_types.push((format!( "resp.headers().get(\"{}\").map(|x| {}(x.to_str().unwrap().parse().unwrap()))", param.name, resource_type_rust_type(r), ), false)); } } else { todo!( "header param type {:?} for {} in {:?}", param.r#type, param.name, input.id ); } } t => todo!("param style {:?}", t), } } // TODO(jelmer): match on media type if let Some(status) = response.status { lines.push(format!( " s if s.as_u16() == reqwest::StatusCode::{} => {{\n", status )); } else { lines.push(" s if s.is_success() => {\n".to_string()); } if !response.representations.is_empty() { lines.push(" let content_type: Option = resp.headers().get(reqwest::header::CONTENT_TYPE).map(|x| x.to_str().unwrap()).map(|x| x.parse().unwrap());\n".to_string()); lines.push( " match content_type.as_ref().map(|x| x.essence_str()) {\n" .to_string(), ); for representation in response.representations.iter() { let media_type = representation .media_type() .unwrap_or(&mime::APPLICATION_JSON); lines.push(format!( " Some(\"{}\") => {{\n", media_type )); let t = match representation { Representation::Definition(_) => None, Representation::Reference(r) => { let rt = representation_rust_type(r); Some(( format!( "resp.json::<{}>(){}?", rt, if config.r#async { ".await" } else { "" } ), true, )) } }; if let Some(t) = t { let mut return_types = return_types.clone(); return_types.insert(0, t); lines.push(format!( " {}\n", serialize_return_types(return_types) )); } else { lines.push(" unimplemented!();\n".to_string()); } lines.push(" }\n".to_string()); } lines.push( " _ => { Err(wadl::Error::UnhandledContentType(content_type)) }\n" .to_string(), ); lines.push(" }\n".to_string()); } else { lines.push(format!( " {}\n", serialize_return_types(return_types) )); } lines.push(" }\n".to_string()); } if input.responses.is_empty() { lines.push(" s if s.is_success() => Ok(()),\n".to_string()); } lines.push(" s => Err(wadl::Error::UnhandledStatus(s))\n".to_string()); lines.push(" }\n".to_string()); lines.push(" }\n".to_string()); lines.push("\n".to_string()); if let Some(extend_method) = config.extend_method.as_ref() { lines.extend(extend_method(parent_id, &name, &ret_type, config)); } lines } fn generate_resource_type( input: &ResourceType, config: &Config, options_names: &HashMap, ) -> Vec { let mut lines = vec![]; for doc in &input.docs { lines.extend(generate_doc(doc, 0, config)); } let name = input.id.as_str(); let name = camel_case_name(name); let visibility = config .resource_type_visibility .as_ref() .and_then(|x| x(name.as_str())) .unwrap_or("pub".to_string()); lines.push(format!( "{}struct {} (reqwest::Url);\n", if visibility.is_empty() { "".to_string() } else { format!("{} ", visibility) }, name )); lines.push("\n".to_string()); lines.push(format!("impl {} {{\n", name)); for method in &input.methods { // Check if we should filter this method if let Some(filter) = config.filter_method.as_ref() { if !filter(method) { continue; } } lines.extend(generate_method( method, input.id.as_str(), config, options_names, )); } lines.push("}\n".to_string()); lines.push("\n".to_string()); lines.push(format!("impl wadl::Resource for {} {{\n", name)); lines.push(" fn url(&self) -> &reqwest::Url {\n".to_string()); lines.push(" &self.0\n".to_string()); lines.push(" }\n".to_string()); lines.push("}\n".to_string()); lines.push("\n".to_string()); lines } #[derive(Default)] #[allow(clippy::type_complexity)] /// Configuration for code generation pub struct Config { /// Whether to generate async code pub r#async: bool, /// Based on the listed type and name of a parameter, determine the rust type pub override_type_name: Option Option>>, /// Support renaming param accessor functions pub param_accessor_rename: Option Option>>, /// Whether to strip code examples from the docstrings /// /// This is useful if the code examples are not valid rust code. pub strip_code_examples: bool, /// Generate custom trait implementations for representations pub generate_representation_traits: Option< Box Option>>, >, /// Return the visibility of a representation pub representation_visibility: Option Option>>, /// Return the visibility of a representation accessor pub accessor_visibility: Option Option>>, /// Return the visibility of a resource type pub resource_type_visibility: Option Option>>, /// Map a method response type to a different type and a function to map the response pub map_type_for_response: Option Option<(String, String)>>>, /// Map an accessor function name to a different type pub map_type_for_accessor: Option Option<(String, String)>>>, /// Extend the generated accessor pub extend_accessor: Option Vec>>, /// Extend the generated method pub extend_method: Option Vec>>, /// Retrieve visibility for a method pub method_visibility: Option Option>>, /// Return whether a param is deprecated pub deprecated_param: Option bool>>, /// Return the name for an enum representation a set of options /// /// The callback can be used to determine if the name is already taken. pub options_enum_name: Option bool>) -> String>>, /// Reformat a docstring; should already be in markdown pub reformat_docstring: Option String>>, /// Convert a string to a multipart Part, given a type name and value pub convert_to_multipart: Option Option>>, /// Check whether a parameter can be nil pub nillable_param: Option bool>>, /// Filter resource types and representations by ID /// Return true to include the resource type/representation pub filter_by_id: Option bool>>, /// Filter representation params/fields /// Return true to include the field/accessor /// This is used to filter out fields that reference filtered-out types pub filter_param: Option bool>>, /// Filter methods /// Return true to include the method /// This is used to filter out methods that reference filtered-out types in parameters pub filter_method: Option bool>>, } impl Config { /// Return identifier of the wadl client pub fn client_trait_name(&self) -> &'static str { if self.r#async { "wadl::r#async::Client" } else { "wadl::blocking::Client" } } /// Check whether the parameter is can be nil pub fn nillable(&self, param: &Param) -> bool { if let Some(nillable_param) = self.nillable_param.as_ref() { nillable_param(param) } else { !param.required } } } fn enum_rust_value(option: &str) -> String { let name = camel_case_name(option.replace(' ', "-").as_str()); // Now, strip all characters not allowed in rust identifiers let name = name .chars() .filter(|c| c.is_alphanumeric() || *c == '_') .collect::(); // If the identifier starts with a digit, prefix it with '_' to make it a valid identifier if name.chars().next().unwrap().is_numeric() { format!("_{}", name) } else { name } } fn generate_options(name: &str, options: &crate::ast::Options) -> Vec { let mut lines = vec![]; lines.push("#[derive(Debug, Clone, Copy, PartialEq, Eq, std::hash::Hash, serde::Serialize, serde::Deserialize)]\n".to_string()); lines.push(format!("pub enum {} {{\n", name)); let mut option_map = HashMap::new(); for option in options.keys() { let rust_name = enum_rust_value(option); lines.push(format!(" #[serde(rename = \"{}\")]\n", option)); lines.push(format!(" {},\n", rust_name)); option_map.insert(option, rust_name); } lines.push("}\n".to_string()); lines.push("\n".to_string()); lines.push(format!("impl std::fmt::Display for {} {{\n", name)); lines.push( " fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n".to_string(), ); lines.push(" match self {\n".to_string()); for (option, rust_name) in option_map { lines.push(format!( " {}::{} => write!(f, \"{}\"),\n", name, rust_name, option )); } lines.push(" }\n".to_string()); lines.push(" }\n".to_string()); lines.push("}\n".to_string()); lines } fn options_rust_enum_name(param: &Param, options: &HashMap) -> String { let mut name = camel_case_name(param.name.as_str()); while options.values().any(|v| v == &name) { name = format!("{}_", name); } name } /// Generate code from a WADL application definition. /// /// This function generates Rust code from a WADL application definition. /// The generated code includes Rust types for the representations and /// resource types defined in the WADL application, as well as methods /// for interacting with the resources. /// /// # Arguments /// * `app` - The WADL application definition. /// * `config` - Configuration for the code generation. pub fn generate(app: &Application, config: &Config) -> String { let mut lines = vec![]; let mut options = HashMap::new(); for param in app.iter_all_params() { if let Some(os) = ¶m.options { if options.contains_key(os) { continue; } let name = if let Some(enum_name_fn) = config.options_enum_name.as_ref() { let cb_options = options.clone(); let name = enum_name_fn( param, Box::new(move |name: &str| -> bool { cb_options.values().any(|v| v == name) }), ); let taken = options .iter() .filter_map(|(k, v)| if v == &name { Some(k) } else { None }) .collect::>(); if !taken.is_empty() { panic!( "Enum name {} is already taken by {:?} ({:?})", name, taken, options ); } name } else { options_rust_enum_name(param, &options) }; let enum_lines = generate_options(name.as_str(), os); options.insert(os.clone(), name); lines.extend(enum_lines); } } for doc in &app.docs { lines.extend(generate_doc(doc, 0, config)); } for representation in &app.representations { if let Some(filter) = config.filter_by_id.as_ref() { if let Some(id) = representation.id.as_ref() { if !filter(id) { continue; } } } lines.extend(generate_representation(representation, config, &options)); } for resource_type in &app.resource_types { if let Some(filter) = config.filter_by_id.as_ref() { if !filter(&resource_type.id) { continue; } } lines.extend(generate_resource_type(resource_type, config, &options)); } lines.concat() } fn indent(indent: usize, lines: impl Iterator) -> impl Iterator { lines.map(move |line| format!("{}{}", " ".repeat(indent * 4), line)) } #[cfg(test)] mod tests { use super::*; #[test] fn test_camel_case_name() { assert_eq!(camel_case_name("foo-bar"), "FooBar"); assert_eq!(camel_case_name("foo-bar-baz"), "FooBarBaz"); assert_eq!(camel_case_name("foo-bar-baz-quux"), "FooBarBazQuux"); assert_eq!(camel_case_name("_foo-bar"), "_fooBar"); assert_eq!(camel_case_name("service-root-json"), "ServiceRootJson"); assert_eq!(camel_case_name("get-some-URL"), "GetSomeURL"); } #[test] fn test_generate_empty() { let input = crate::ast::Application { docs: vec![], representations: vec![], resource_types: vec![], resources: vec![], grammars: vec![], }; let config = Config::default(); let lines = generate(&input, &config); assert_eq!(lines, "".to_string()); } #[test] fn test_enum_rust_value() { assert_eq!(enum_rust_value("foo"), "Foo"); assert_eq!(enum_rust_value("foo bar"), "FooBar"); assert_eq!(enum_rust_value("foo bar blah"), "FooBarBlah"); assert_eq!(enum_rust_value("foo-bar"), "FooBar"); } #[test] fn test_snake_case_name() { assert_eq!(snake_case_name("F"), "f"); assert_eq!(snake_case_name("FooBar"), "foo_bar"); assert_eq!(snake_case_name("FooBarBaz"), "foo_bar_baz"); assert_eq!(snake_case_name("FooBarBazQuux"), "foo_bar_baz_quux"); assert_eq!(snake_case_name("_FooBar"), "_foo_bar"); assert_eq!(snake_case_name("ServiceRootJson"), "service_root_json"); assert_eq!(snake_case_name("GetSomeURL"), "get_some_url"); } #[test] fn test_strip_code_examples() { let input = r#"This is a test ```python def foo(): pass ``` This is another test ```python def bar(): pass ``` "#; let expected = r#"This is a test This is another test"#; assert_eq!(strip_code_examples(input.to_string()), expected); } #[test] fn test_format_doc_plain() { let doc = Doc { title: None, lang: None, content: "This is a test".to_string(), xmlns: None, }; assert_eq!( format_doc(&doc, &Config::default()), "This is a test".to_string() ); } #[test] fn test_format_doc_html() { let doc = Doc { title: None, lang: None, content: "

This is a test

".to_string(), xmlns: Some("http://www.w3.org/1999/xhtml".parse().unwrap()), }; assert_eq!( format_doc(&doc, &Config::default()), "This is a test".to_string() ); } #[test] fn test_format_doc_html_link() { let doc = Doc { title: None, lang: None, content: "

This is a test

".to_string(), xmlns: Some("http://www.w3.org/1999/xhtml".parse().unwrap()), }; assert_eq!( format_doc(&doc, &Config::default()), "This is a [test](https://example.com)".to_string() ); } #[test] fn test_generate_doc_plain() { let doc = Doc { title: Some("Foo".to_string()), lang: None, content: "This is a test".to_string(), xmlns: None, }; assert_eq!( generate_doc(&doc, 0, &Config::default()), vec![ "/// # Foo\n".to_string(), "///\n".to_string(), "/// This is a test\n".to_string(), ] ); } #[test] fn test_generate_doc_html() { let doc = Doc { title: Some("Foo".to_string()), lang: None, content: "

This is a test

".to_string(), xmlns: Some("http://www.w3.org/1999/xhtml".parse().unwrap()), }; assert_eq!( generate_doc(&doc, 0, &Config::default()), vec![ "/// # Foo\n".to_string(), "///\n".to_string(), "/// This is a test\n".to_string(), ] ); } #[test] fn test_generate_doc_multiple_lines() { let doc = Doc { title: Some("Foo".to_string()), lang: None, content: "This is a test\n\nThis is another test".to_string(), xmlns: None, }; assert_eq!( generate_doc(&doc, 0, &Config::default()), vec![ "/// # Foo\n".to_string(), "///\n".to_string(), "/// This is a test\n".to_string(), "///\n".to_string(), "/// This is another test\n".to_string(), ] ); } #[test] fn test_resource_type_rust_type() { use std::str::FromStr; let rt = ResourceTypeRef::from_str("https://api.launchpad.net/1.0/#person").unwrap(); assert_eq!(resource_type_rust_type(&rt), "Person"); } #[test] fn test_param_rust_type() { use std::str::FromStr; let rt = ResourceTypeRef::from_str("https://api.launchpad.net/1.0/#person").unwrap(); let mut param = Param { name: "person".to_string(), r#type: "string".to_string(), required: true, repeating: false, fixed: None, doc: None, options: None, id: None, style: ParamStyle::Plain, path: None, links: vec![crate::ast::Link { resource_type: Some(rt), relation: None, reverse_relation: None, doc: None, }], }; let method = Method { docs: vec![], id: "getPerson".to_string(), name: "getPerson".to_string(), request: Request { docs: vec![], params: vec![param.clone()], representations: vec![], }, responses: vec![Response { status: None, docs: vec![], params: vec![param.clone()], representations: vec![], }], }; let container = ParamContainer::Request(&method, &method.request); let (param_type, _) = param_rust_type( &container, ¶m, &Config::default(), resource_type_rust_type, &HashMap::new(), ); assert_eq!(param_type, "Person"); param.required = false; let (param_type, _) = param_rust_type( &container, ¶m, &Config::default(), resource_type_rust_type, &HashMap::new(), ); assert_eq!(param_type, "Option"); param.repeating = true; param.required = true; let (param_type, _) = param_rust_type( &container, ¶m, &Config::default(), resource_type_rust_type, &HashMap::new(), ); assert_eq!(param_type, "Vec"); param.repeating = false; param.r#type = "string".to_string(); param.links = vec![]; let (param_type, _) = param_rust_type( &container, ¶m, &Config::default(), resource_type_rust_type, &HashMap::new(), ); assert_eq!(param_type, "String"); param.r#type = "binary".to_string(); let (param_type, _) = param_rust_type( &container, ¶m, &Config::default(), resource_type_rust_type, &HashMap::new(), ); assert_eq!(param_type, "Vec"); param.r#type = "xsd:date".to_string(); let (param_type, _) = param_rust_type( &container, ¶m, &Config::default(), resource_type_rust_type, &HashMap::new(), ); assert_eq!(param_type, "chrono::NaiveDate"); param.r#type = "string".to_string(); param.options = Some(Options::from(vec!["one".to_string(), "two".to_string()])); let (param_type, _) = param_rust_type( &container, ¶m, &Config::default(), resource_type_rust_type, &maplit::hashmap! { Options::from(vec!["one".to_string(), "two".to_string()]) => "MyOptions".to_string(), }, ); assert_eq!(param_type, "MyOptions"); } #[test] fn test_readonly_rust_type() { assert_eq!(readonly_rust_type("String"), "&str"); assert_eq!(readonly_rust_type("Vec"), "&[String]"); assert_eq!( readonly_rust_type("Option>"), "Option<&[String]>" ); assert_eq!(readonly_rust_type("Option"), "Option<&str>"); assert_eq!(readonly_rust_type("usize"), "&usize"); } #[test] fn test_escape_rust_reserved() { assert_eq!(escape_rust_reserved("type"), "r#type"); assert_eq!(escape_rust_reserved("match"), "r#match"); assert_eq!(escape_rust_reserved("move"), "r#move"); assert_eq!(escape_rust_reserved("use"), "r#use"); assert_eq!(escape_rust_reserved("loop"), "r#loop"); assert_eq!(escape_rust_reserved("continue"), "r#continue"); assert_eq!(escape_rust_reserved("break"), "r#break"); assert_eq!(escape_rust_reserved("fn"), "r#fn"); assert_eq!(escape_rust_reserved("struct"), "r#struct"); assert_eq!(escape_rust_reserved("enum"), "r#enum"); assert_eq!(escape_rust_reserved("trait"), "r#trait"); assert_eq!(escape_rust_reserved("impl"), "r#impl"); assert_eq!(escape_rust_reserved("pub"), "r#pub"); assert_eq!(escape_rust_reserved("as"), "r#as"); assert_eq!(escape_rust_reserved("const"), "r#const"); assert_eq!(escape_rust_reserved("let"), "r#let"); assert_eq!(escape_rust_reserved("foo"), "foo"); } #[test] fn test_representation_rust_type() { let rt = RepresentationRef::Id("person".to_string()); assert_eq!(representation_rust_type(&rt), "Person"); } #[test] fn test_generate_representation() { let input = RepresentationDef { media_type: Some("application/json".parse().unwrap()), element: None, profile: None, docs: vec![], id: Some("person".to_string()), params: vec![ Param { name: "name".to_string(), r#type: "string".to_string(), style: ParamStyle::Plain, required: true, doc: Some(Doc::new("The name of the person".to_string())), path: None, id: None, repeating: false, fixed: None, links: vec![], options: None, }, Param { name: "age".to_string(), r#type: "xs:int".to_string(), required: true, doc: Some(Doc::new("The age of the person".to_string())), style: ParamStyle::Query, path: None, id: None, repeating: false, fixed: None, links: vec![], options: None, }, ], }; let config = Config::default(); let lines = generate_representation_struct_json(&input, &config, &HashMap::new()); assert_eq!( lines, vec![ "/// Representation of the `person` resource\n".to_string(), "#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]\n" .to_string(), "pub struct Person {\n".to_string(), " // was: string\n".to_string(), " /// The name of the person\n".to_string(), " pub name: String,\n".to_string(), "\n".to_string(), " // was: xs:int\n".to_string(), " /// The age of the person\n".to_string(), " pub age: i32,\n".to_string(), "\n".to_string(), "}\n".to_string(), "\n".to_string(), ] ); } #[test] fn test_supported_representation_def() { let mut d = RepresentationDef { media_type: Some(crate::WADL_MIME_TYPE.parse().unwrap()), ..Default::default() }; assert!(!supported_representation_def(&d)); d.media_type = Some(XHTML_MIME_TYPE.parse().unwrap()); assert!(!supported_representation_def(&d)); d.media_type = Some("application/json".parse().unwrap()); assert!(!supported_representation_def(&d)); } #[test] fn test_rust_type_for_response() { let mut input = Response { params: vec![Param { id: Some("foo".to_string()), name: "foo".to_string(), r#type: "string".to_string(), style: ParamStyle::Header, doc: None, required: true, repeating: false, fixed: None, path: None, links: Vec::new(), options: None, }], ..Default::default() }; let method = Method { name: "GET".to_string(), id: "get".to_string(), docs: Vec::new(), request: Request::default(), responses: vec![input.clone()], }; assert_eq!( rust_type_for_response(&method, &input, "foo", &HashMap::new()), "String".to_string() ); input.params = vec![ Param { id: Some("foo".to_string()), name: "foo".to_string(), r#type: "string".to_string(), style: ParamStyle::Header, doc: None, required: true, repeating: false, fixed: None, path: None, links: Vec::new(), options: None, }, Param { id: Some("bar".to_string()), name: "bar".to_string(), r#type: "string".to_string(), style: ParamStyle::Header, doc: None, required: true, repeating: false, fixed: None, path: None, links: Vec::new(), options: None, }, ]; assert_eq!( rust_type_for_response(&method, &input, "foo", &HashMap::new()), "(String, String)".to_string() ); input.params = vec![Param { id: Some("foo".to_string()), name: "foo".to_string(), r#type: "string".to_string(), style: ParamStyle::Header, doc: None, required: true, repeating: false, fixed: None, path: None, links: vec![Link { relation: None, reverse_relation: None, resource_type: Some("http://example.com/#foo".parse().unwrap()), doc: None, }], options: None, }]; assert_eq!( rust_type_for_response(&method, &input, "foo", &HashMap::new()), "Foo".to_string() ); input.params = vec![Param { id: Some("foo".to_string()), name: "foo".to_string(), r#type: "string".to_string(), style: ParamStyle::Header, doc: None, required: true, repeating: false, fixed: None, path: None, links: vec![Link { relation: None, reverse_relation: None, resource_type: Some("http://example.com/#foo".parse().unwrap()), doc: None, }], options: None, }]; assert_eq!( rust_type_for_response(&method, &input, "foo", &HashMap::new()), "Foo".to_string() ); input.params = vec![Param { id: None, name: "foo".to_string(), r#type: "string".to_string(), style: ParamStyle::Header, doc: None, required: true, repeating: false, fixed: None, options: None, path: None, links: vec![Link { relation: None, reverse_relation: None, resource_type: None, doc: None, }], }]; assert_eq!( rust_type_for_response(&method, &input, "foo", &HashMap::new()), "url::Url".to_string() ); } #[test] fn test_format_arg_doc() { let config = Config::default(); assert_eq!( format_arg_doc("foo", None, &config), vec![" /// * `foo`\n".to_string()] ); assert_eq!( format_arg_doc("foo", Some(&Doc::new("bar".to_string())), &config), vec![" /// * `foo`: bar\n".to_string()] ); assert_eq!( format_arg_doc("foo", Some(&Doc::new("bar\nbaz".to_string())), &config), vec![ " /// * `foo`: bar\n".to_string(), " /// baz\n".to_string() ] ); assert_eq!( format_arg_doc("foo", Some(&Doc::new("bar\n\nbaz".to_string())), &config), vec![ " /// * `foo`: bar\n".to_string(), " ///\n".to_string(), " /// baz\n".to_string() ] ); } #[test] fn test_apply_map_fn() { assert_eq!(apply_map_fn(None, "x", false), "x".to_string()); assert_eq!( apply_map_fn(Some("Some"), "x", false), "Some(x)".to_string() ); assert_eq!( apply_map_fn(Some("Some"), "x", true), "x.map(Some)".to_string() ); assert_eq!( apply_map_fn(Some("|y|y+1"), "x", false), "(|y|y+1)(x)".to_string() ); assert_eq!( apply_map_fn(Some("|y|y+1"), "x", true), "x.map(|y|y+1)".to_string() ); } #[test] fn test_generate_method() { let input = Method { id: "foo".to_string(), name: "GET".to_string(), docs: vec![], request: Request { docs: vec![], params: vec![], representations: vec![], }, responses: vec![], }; let config = Config::default(); let lines = generate_method(&input, "bar", &config, &HashMap::new()); assert_eq!(lines, vec![ " /// Retrieve the resource.\n".to_string(), " ///\n".to_string(), " pub fn foo<'a>(&self, client: &'a dyn wadl::blocking::Client) -> std::result::Result<(), wadl::Error> {\n".to_string(), " let mut url_ = self.url().clone();\n".to_string(), "\n".to_string(), " let mut req = client.request(reqwest::Method::GET, url_);\n".to_string(), "\n".to_string(), " let resp = req.send()?;\n".to_string(), " match resp.status() {\n".to_string(), " s if s.is_success() => Ok(()),\n".to_string(), " s => Err(wadl::Error::UnhandledStatus(s))\n".to_string(), " }\n".to_string(), " }\n".to_string(), "\n".to_string(), ]); } #[test] fn test_generate_resource_type() { let input = ResourceType { id: "foo".to_string(), docs: vec![], methods: vec![], query_type: mime::APPLICATION_JSON, params: vec![], subresources: vec![], }; let config = Config::default(); let lines = generate_resource_type(&input, &config, &HashMap::new()); assert_eq!( lines, vec![ "pub struct Foo (reqwest::Url);\n".to_string(), "\n".to_string(), "impl Foo {\n".to_string(), "}\n".to_string(), "\n".to_string(), "impl wadl::Resource for Foo {\n".to_string(), " fn url(&self) -> &reqwest::Url {\n".to_string(), " &self.0\n".to_string(), " }\n".to_string(), "}\n".to_string(), "\n".to_string(), ] ); } } wadl-0.5.3/src/lib.rs000064400000000000000000000120621046102023000124640ustar 00000000000000#![deny(missing_docs)] //! # WADL //! //! A crate for parsing WADL files and generating Rust code from them. pub mod ast; #[cfg(feature = "codegen")] pub mod codegen; mod parse; /// The MIME type of WADL files. pub const WADL_MIME_TYPE: &str = "application/vnd.sun.wadl+xml"; pub use parse::{parse, parse_bytes, parse_file, parse_string, Error as ParseError}; use url::Url; /// The root of the web service. pub trait Resource { /// The URL of the resource fn url(&self) -> &Url; } #[cfg(feature = "async")] /// Asynchronous features pub mod r#async { use super::*; /// A client for a WADL API #[async_trait::async_trait] pub trait Client: Sync + Send { /// Create a new request builder async fn request(&self, method: reqwest::Method, url: url::Url) -> reqwest::RequestBuilder; } #[async_trait::async_trait] impl Client for reqwest::Client { async fn request(&self, method: reqwest::Method, url: url::Url) -> reqwest::RequestBuilder { self.request(method, url) } } /// Get the WADL AST from a URL. pub async fn get_wadl_resource_by_href( client: &dyn Client, href: &url::Url, ) -> Result { let mut req = client.request(reqwest::Method::GET, href.clone()).await; req = req.header(reqwest::header::ACCEPT, super::WADL_MIME_TYPE); let res = req.send().await?; let text = res.text().await?; let application = super::parse_string(&text)?; let resource = application.get_resource_by_href(href).unwrap(); Ok(resource.clone()) } } #[cfg(feature = "blocking")] /// Blocking features pub mod blocking { use super::*; /// A client for a WADL API pub trait Client { /// Create a new request builder fn request( &self, method: reqwest::Method, url: url::Url, ) -> reqwest::blocking::RequestBuilder; } impl Client for reqwest::blocking::Client { fn request( &self, method: reqwest::Method, url: url::Url, ) -> reqwest::blocking::RequestBuilder { self.request(method, url) } } /// Get the WADL AST from a URL. pub fn get_wadl_resource_by_href( client: &dyn Client, href: &url::Url, ) -> Result { let mut req = client.request(reqwest::Method::GET, href.clone()); req = req.header(reqwest::header::ACCEPT, WADL_MIME_TYPE); let res = req.send()?; let text = res.text()?; let application = parse_string(&text)?; let resource = application.get_resource_by_href(href).unwrap(); Ok(resource.clone()) } } #[derive(Debug)] /// The error type for this crate. pub enum Error { /// The URL is invalid. InvalidUrl, /// A reqwest error occurred. Reqwest(reqwest::Error), /// The URL could not be parsed. Url(url::ParseError), /// The JSON could not be parsed. Json(serde_json::Error), /// The WADL could not be parsed. Wadl(ParseError), /// The response status was not handled by the library. UnhandledStatus(reqwest::StatusCode), /// The response content type was not handled by the library. UnhandledContentType(Option), /// An I/O error occurred. Io(std::io::Error), } impl From for Error { fn from(err: std::io::Error) -> Self { Error::Io(err) } } impl From for Error { fn from(err: serde_json::Error) -> Self { Error::Json(err) } } impl std::fmt::Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match self { Error::InvalidUrl => write!(f, "Invalid URL"), Error::Reqwest(err) => write!(f, "Reqwest error: {}", err), Error::Url(err) => write!(f, "URL error: {}", err), Error::Json(err) => write!(f, "JSON error: {}", err), Error::Wadl(err) => write!(f, "WADL error: {}", err), Error::UnhandledContentType(Some(c)) => write!(f, "Unhandled content type: {}", c), Error::UnhandledContentType(None) => write!(f, "No content type"), Error::UnhandledStatus(s) => write!(f, "Unhandled status: {}", s), Error::Io(err) => write!(f, "IO error: {}", err), } } } impl std::error::Error for Error { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { Error::Reqwest(err) => Some(err), Error::Url(err) => Some(err), Error::Json(err) => Some(err), Error::Wadl(err) => Some(err), Error::Io(err) => Some(err), _ => None, } } } impl From for Error { fn from(err: reqwest::Error) -> Self { Error::Reqwest(err) } } impl From for Error { fn from(err: url::ParseError) -> Self { Error::Url(err) } } impl From for Error { fn from(err: ParseError) -> Self { Error::Wadl(err) } } wadl-0.5.3/src/parse.rs000064400000000000000000000757701046102023000130470ustar 00000000000000use crate::ast::*; use iri_string::spec::IriSpec; use iri_string::types::RiReferenceString; use std::io::Read; use xmltree::Element; #[allow(unused)] /// The namespace of the WADL XML schema. pub const WADL_NS: &str = "http://wadl.dev.java.net/2009/02"; #[derive(Debug)] /// Errors that can occur while parsing a WADL document. pub enum Error { /// An I/O error occurred while reading the document. Io(std::io::Error), /// An error occurred while parsing the XML document. Xml(xmltree::ParseError), /// An error occurred while parsing a URL. Url(url::ParseError), /// An error occurred while parsing a MIME type. Mime(mime::FromStrError), } impl From for Error { fn from(e: std::io::Error) -> Self { Error::Io(e) } } impl From for Error { fn from(e: xmltree::ParseError) -> Self { Error::Xml(e) } } impl From for Error { fn from(e: url::ParseError) -> Self { Error::Url(e) } } impl From for Error { fn from(e: mime::FromStrError) -> Self { Error::Mime(e) } } impl std::fmt::Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match &self { Error::Io(e) => write!(f, "IO error: {}", e), Error::Xml(e) => write!(f, "XML error: {}", e), Error::Url(e) => write!(f, "URL error: {}", e), Error::Mime(e) => write!(f, "MIME error: {}", e), } } } impl std::error::Error for Error {} pub fn parse_options(element: &Element) -> Option { let mut options = Options::new(); for option_node in &element.children { if let Some(element) = option_node.as_element() { if element.name == "option" { let value = element.attributes.get("value").cloned(); let media_type = element .attributes .get("mediaType") .cloned() .map(|x| x.parse().unwrap()); options.insert(value.unwrap(), media_type); } } } if options.is_empty() { None } else { Some(options) } } #[test] fn test_parse_options() { let xml = r#"