wadl-0.3.0/.cargo_vcs_info.json0000644000000001360000000000100117630ustar { "git": { "sha1": "aebae3e9d6d44296b09f6994988fc68d179f3bc4" }, "path_in_vcs": "" }wadl-0.3.0/.github/CODEOWNERS000064400000000000000000000000121046102023000134770ustar 00000000000000* @jelmer wadl-0.3.0/.github/FUNDING.yml000064400000000000000000000000171046102023000137260ustar 00000000000000github: jelmer wadl-0.3.0/.github/dependabot.yml000064400000000000000000000006251046102023000147460ustar 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: "weekly" rebase-strategy: "disabled" - package-ecosystem: "github-actions" directory: "/" schedule: interval: weekly wadl-0.3.0/.github/workflows/rust.yml000064400000000000000000000004121046102023000156650ustar 00000000000000name: Rust on: push: pull_request: env: CARGO_TERM_COLOR: always jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Build run: cargo build --verbose - name: Run tests run: cargo test --verbose wadl-0.3.0/.gitignore000064400000000000000000000000121046102023000125340ustar 00000000000000*~ target wadl-0.3.0/Cargo.lock0000644000001351460000000000100077500ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "addr2line" version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ "gimli", ] [[package]] name = "adler" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aho-corasick" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] [[package]] name = "anstream" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", "utf8parse", ] [[package]] name = "anstyle" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" [[package]] name = "anstyle-parse" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" dependencies = [ "anstyle", "windows-sys 0.52.0", ] [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" dependencies = [ "addr2line", "cc", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", ] [[package]] name = "base64" version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[package]] name = "bumpalo" version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "bytes" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "cc" version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" dependencies = [ "libc", ] [[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.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" version = "4.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2" dependencies = [ "clap_builder", "clap_derive", ] [[package]] name = "clap_builder" version = "4.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb" dependencies = [ "anstream", "anstyle", "clap_lex", "strsim", ] [[package]] name = "clap_derive" version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" dependencies = [ "heck", "proc-macro2", "quote", "syn 2.0.43", ] [[package]] name = "clap_lex" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" [[package]] name = "colorchoice" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "combine" version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" 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.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "encoding_rs" version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" dependencies = [ "cfg-if", ] [[package]] name = "env_logger" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece" dependencies = [ "humantime", "is-terminal", "log", "regex", "termcolor", ] [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", "windows-sys 0.52.0", ] [[package]] name = "fastrand" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[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.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 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.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" dependencies = [ "futures-core", "futures-sink", ] [[package]] name = "futures-core" version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" [[package]] name = "futures-io" version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" [[package]] name = "futures-sink" version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" [[package]] name = "futures-task" version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" [[package]] name = "futures-util" version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" dependencies = [ "futures-core", "futures-io", "futures-sink", "futures-task", "memchr", "pin-project-lite", "pin-utils", "slab", ] [[package]] name = "getrandom" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" dependencies = [ "cfg-if", "libc", "wasi", ] [[package]] name = "gimli" version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "h2" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51ee2dd2e4f378392eeff5d51618cd9a63166a2513846bbc55f21cfacd9199d4" dependencies = [ "bytes", "fnv", "futures-core", "futures-sink", "futures-util", "http", "indexmap", "slab", "tokio", "tokio-util", "tracing", ] [[package]] name = "hashbrown" version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" [[package]] name = "heck" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" [[package]] name = "html2md" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be92446e11d68f5d71367d571c229d09ced1f24ab6d08ea0bff329d5f6c0b2a3" dependencies = [ "html5ever", "jni", "lazy_static", "markup5ever_rcdom", "percent-encoding", "regex", ] [[package]] name = "html5ever" version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" dependencies = [ "log", "mac", "markup5ever", "proc-macro2", "quote", "syn 1.0.109", ] [[package]] name = "http" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" dependencies = [ "bytes", "fnv", "itoa", ] [[package]] name = "http-body" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" dependencies = [ "bytes", "http", ] [[package]] name = "http-body-util" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" dependencies = [ "bytes", "futures-core", "http", "http-body", "pin-project-lite", ] [[package]] name = "httparse" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "186548d73ac615b32a73aafe38fb4f56c0d340e110e5a200bcadbaf2e199263a" dependencies = [ "bytes", "futures-channel", "futures-util", "h2", "http", "http-body", "httparse", "itoa", "pin-project-lite", "smallvec", "tokio", "want", ] [[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.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" dependencies = [ "bytes", "futures-channel", "futures-util", "http", "http-body", "hyper", "pin-project-lite", "socket2", "tokio", "tower", "tower-service", "tracing", ] [[package]] name = "idna" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ "unicode-bidi", "unicode-normalization", ] [[package]] name = "indexmap" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", "hashbrown", ] [[package]] name = "ipnet" version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "iri-string" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21859b667d66a4c1dacd9df0863b3efb65785474255face87f5bca39dd8407c0" dependencies = [ "memchr", "serde", ] [[package]] name = "is-terminal" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi", "rustix", "windows-sys 0.48.0", ] [[package]] name = "itoa" version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[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.66" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" dependencies = [ "wasm-bindgen", ] [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" version = "0.2.151" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" [[package]] name = "linux-raw-sys" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" [[package]] name = "lock_api" version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", ] [[package]] name = "log" version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[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.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" dependencies = [ "log", "phf", "phf_codegen", "string_cache", "string_cache_codegen", "tendril", ] [[package]] name = "markup5ever_rcdom" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9521dd6750f8e80ee6c53d65e2e4656d7de37064f3a7a5d2d11d05df93839c2" dependencies = [ "html5ever", "markup5ever", "tendril", "xml5ever", ] [[package]] name = "memchr" version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[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.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" dependencies = [ "mime", "unicase", ] [[package]] name = "miniz_oxide" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ "adler", ] [[package]] name = "mio" version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", "wasi", "windows-sys 0.48.0", ] [[package]] name = "native-tls" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" dependencies = [ "lazy_static", "libc", "log", "openssl", "openssl-probe", "openssl-sys", "schannel", "security-framework", "security-framework-sys", "tempfile", ] [[package]] name = "new_debug_unreachable" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" [[package]] name = "num_cpus" version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ "hermit-abi", "libc", ] [[package]] name = "object" version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" dependencies = [ "memchr", ] [[package]] name = "once_cell" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "openssl" version = "0.10.61" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b8419dc8cc6d866deb801274bba2e6f8f6108c1bb7fcc10ee5ab864931dbb45" dependencies = [ "bitflags 2.4.1", "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 2.0.43", ] [[package]] name = "openssl-probe" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" version = "0.9.97" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3eaad34cdd97d81de97964fc7f29e2d104f483840d906ef56daa1912338460b" dependencies = [ "cc", "libc", "pkg-config", "vcpkg", ] [[package]] name = "parking_lot" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", "parking_lot_core", ] [[package]] name = "parking_lot_core" version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", "windows-targets 0.48.5", ] [[package]] name = "percent-encoding" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "phf" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" dependencies = [ "phf_shared", ] [[package]] name = "phf_codegen" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" dependencies = [ "phf_generator", "phf_shared", ] [[package]] name = "phf_generator" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" dependencies = [ "phf_shared", "rand", ] [[package]] name = "phf_shared" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" dependencies = [ "siphasher", ] [[package]] name = "pin-project" version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", "syn 2.0.43", ] [[package]] name = "pin-project-lite" version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[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.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "ppv-lite86" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[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.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] [[package]] name = "rand" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", "rand_core", ] [[package]] name = "rand_chacha" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", "rand_core", ] [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom", ] [[package]] name = "redox_syscall" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ "bitflags 1.3.2", ] [[package]] name = "regex" version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "reqwest" version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d66674f2b6fb864665eea7a3c1ac4e3dfacd2fda83cf6f935a612e01b0e3338" dependencies = [ "base64", "bytes", "encoding_rs", "futures-channel", "futures-core", "futures-util", "h2", "http", "http-body", "http-body-util", "hyper", "hyper-tls", "hyper-util", "ipnet", "js-sys", "log", "mime", "mime_guess", "native-tls", "once_cell", "percent-encoding", "pin-project-lite", "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", "sync_wrapper", "system-configuration", "tokio", "tokio-native-tls", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", "winreg", ] [[package]] name = "rustc-demangle" version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustix" version = "0.38.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" dependencies = [ "bitflags 2.4.1", "errno", "libc", "linux-raw-sys", "windows-sys 0.52.0", ] [[package]] name = "rustls-pemfile" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ "base64", ] [[package]] name = "ryu" version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" [[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.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ "windows-sys 0.48.0", ] [[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.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", "security-framework-sys", ] [[package]] name = "security-framework-sys" version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" dependencies = [ "core-foundation-sys", "libc", ] [[package]] name = "serde" version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", "syn 2.0.43", ] [[package]] name = "serde_json" version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ "itoa", "ryu", "serde", ] [[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 = "siphasher" version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" [[package]] name = "slab" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] [[package]] name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" dependencies = [ "libc", "windows-sys 0.48.0", ] [[package]] name = "string_cache" version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" dependencies = [ "new_debug_unreachable", "once_cell", "parking_lot", "phf_shared", "precomputed-hash", "serde", ] [[package]] name = "string_cache_codegen" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" dependencies = [ "phf_generator", "phf_shared", "proc-macro2", "quote", ] [[package]] name = "strsim" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "syn" version = "2.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee659fb5f3d355364e1f3e5bc10fb82068efbf824a1e9d1c9504244a6469ad53" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "sync_wrapper" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" [[package]] name = "system-configuration" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ "bitflags 1.3.2", "core-foundation", "system-configuration-sys", ] [[package]] name = "system-configuration-sys" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" dependencies = [ "core-foundation-sys", "libc", ] [[package]] name = "tempfile" version = "3.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" dependencies = [ "cfg-if", "fastrand", "redox_syscall", "rustix", "windows-sys 0.48.0", ] [[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 = "termcolor" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" dependencies = [ "winapi-util", ] [[package]] name = "thiserror" version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df" dependencies = [ "proc-macro2", "quote", "syn 2.0.43", ] [[package]] name = "tinyvec" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ "tinyvec_macros", ] [[package]] name = "tinyvec_macros" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" version = "1.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d45b238a16291a4e1584e61820b8ae57d696cc5015c459c229ccc6990cc1c" dependencies = [ "backtrace", "bytes", "libc", "mio", "num_cpus", "pin-project-lite", "socket2", "windows-sys 0.48.0", ] [[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-util" version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" dependencies = [ "bytes", "futures-core", "futures-sink", "pin-project-lite", "tokio", "tracing", ] [[package]] name = "tower" version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" dependencies = [ "futures-core", "futures-util", "pin-project", "pin-project-lite", "tokio", "tower-layer", "tower-service", "tracing", ] [[package]] name = "tower-layer" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" [[package]] name = "tower-service" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ "log", "pin-project-lite", "tracing-core", ] [[package]] name = "tracing-core" version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" 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.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" dependencies = [ "version_check", ] [[package]] name = "unicode-bidi" version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" dependencies = [ "tinyvec", ] [[package]] name = "url" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", "idna", "percent-encoding", ] [[package]] name = "utf-8" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] name = "utf8parse" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "vcpkg" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wadl" version = "0.3.0" dependencies = [ "clap", "env_logger", "form_urlencoded", "html2md", "iri-string", "lazy_static", "log", "maplit", "mime", "proc-macro2", "quote", "reqwest", "serde_json", "syn 2.0.43", "url", "xmltree", ] [[package]] name = "walkdir" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" 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.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" dependencies = [ "cfg-if", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", "syn 2.0.43", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" dependencies = [ "cfg-if", "js-sys", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", "syn 2.0.43", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" [[package]] name = "web-sys" version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" dependencies = [ "winapi", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ "windows-targets 0.48.5", ] [[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.0", ] [[package]] name = "windows-targets" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ "windows_aarch64_gnullvm 0.48.5", "windows_aarch64_msvc 0.48.5", "windows_i686_gnu 0.48.5", "windows_i686_msvc 0.48.5", "windows_x86_64_gnu 0.48.5", "windows_x86_64_gnullvm 0.48.5", "windows_x86_64_msvc 0.48.5", ] [[package]] name = "windows-targets" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" dependencies = [ "windows_aarch64_gnullvm 0.52.0", "windows_aarch64_msvc 0.52.0", "windows_i686_gnu 0.52.0", "windows_i686_msvc 0.52.0", "windows_x86_64_gnu 0.52.0", "windows_x86_64_gnullvm 0.52.0", "windows_x86_64_msvc 0.52.0", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winreg" version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ "cfg-if", "windows-sys 0.48.0", ] [[package]] name = "xml-rs" version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a" [[package]] name = "xml5ever" version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4034e1d05af98b51ad7214527730626f019682d797ba38b51689212118d8e650" dependencies = [ "log", "mac", "markup5ever", ] [[package]] name = "xmltree" version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7d8a75eaf6557bb84a65ace8609883db44a29951042ada9b393151532e41fcb" dependencies = [ "xml-rs", ] wadl-0.3.0/Cargo.toml0000644000000035220000000000100077630ustar # 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.3.0" authors = ["Jelmer Vernooij "] default-run = "wadlc" description = "A WADL parser for Rust" readme = "README.md" license = "Apache-2.0" repository = "https://github.com/jelmer/wadl" [[bin]] name = "wadlc" path = "src/bin/wadlc.rs" required-features = ["cli"] [dependencies.clap] version = "4.4.6" features = [ "derive", "env", ] optional = true [dependencies.env_logger] version = "0.10.0" optional = true [dependencies.form_urlencoded] version = "1.2.1" [dependencies.html2md] version = "0.2.14" optional = true [dependencies.iri-string] version = "0.7.0" features = ["std"] [dependencies.lazy_static] version = "1.4.0" [dependencies.log] version = "0.4.21" [dependencies.mime] version = "0.3.17" [dependencies.proc-macro2] version = "1" optional = true [dependencies.quote] version = "1" optional = true [dependencies.reqwest] version = "0.12.2" features = [ "blocking", "json", "multipart", ] [dependencies.serde_json] version = "1.0.107" [dependencies.syn] version = "2" optional = true [dependencies.url] version = "2.4" [dependencies.xmltree] version = "0.10.3" [dev-dependencies.maplit] version = "1.0.2" [features] cli = [ "dep:clap", "dep:env_logger", "codegen", ] codegen = [ "dep:proc-macro2", "dep:quote", "dep:syn", "dep:html2md", ] default = ["cli"] wadl-0.3.0/Cargo.toml.orig0000644000000021070000000000100107200ustar [package] name = "wadl" version = "0.3.0" 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.4.6", features = ["derive", "env"], optional = true } env_logger = { version = "0.10.0", optional = true } form_urlencoded = "1.2.1" html2md = { version = "0.2.14", optional = true } lazy_static = "1.4.0" log = "0.4.21" mime = "0.3.17" proc-macro2 = { version = "1", optional = true } quote = { version = "1", optional = true } reqwest = { version = "0.12.2", features = ["blocking", "json", "multipart"] } serde_json = "1.0.107" syn = { version = "2", optional = true } url = "2.4" xmltree = "0.10.3" iri-string = { version = "0.7.0", features = ["std"] } [features] default = ["cli"] codegen = ["dep:proc-macro2", "dep:quote", "dep:syn", "dep:html2md"] cli = ["dep:clap", "dep:env_logger", "codegen"] [[bin]] name = "wadlc" path = "src/bin/wadlc.rs" required-features = ["cli"] [dev-dependencies] maplit = "1.0.2" wadl-0.3.0/Cargo.toml.orig000064400000000000000000000021071046102023000134420ustar 00000000000000[package] name = "wadl" version = "0.3.0" 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.4.6", features = ["derive", "env"], optional = true } env_logger = { version = "0.10.0", optional = true } form_urlencoded = "1.2.1" html2md = { version = "0.2.14", optional = true } lazy_static = "1.4.0" log = "0.4.21" mime = "0.3.17" proc-macro2 = { version = "1", optional = true } quote = { version = "1", optional = true } reqwest = { version = "0.12.2", features = ["blocking", "json", "multipart"] } serde_json = "1.0.107" syn = { version = "2", optional = true } url = "2.4" xmltree = "0.10.3" iri-string = { version = "0.7.0", features = ["std"] } [features] default = ["cli"] codegen = ["dep:proc-macro2", "dep:quote", "dep:syn", "dep:html2md"] cli = ["dep:clap", "dep:env_logger", "codegen"] [[bin]] name = "wadlc" path = "src/bin/wadlc.rs" required-features = ["cli"] [dev-dependencies] maplit = "1.0.2" wadl-0.3.0/README.md000064400000000000000000000024531046102023000120360ustar 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.3.0/disperse.conf000064400000000000000000000001231046102023000132340ustar 00000000000000timeout_days: 5 tag_name: "v$VERSION" github_url: "https://github.com/jelmer/wadl" wadl-0.3.0/src/ast.rs000064400000000000000000000345021046102023000125030ustar 00000000000000use std::collections::HashMap; use iri_string::spec::IriSpec; use iri_string::types::{RiReferenceString}; use url::Url; pub type Id = String; #[derive(Debug, PartialEq, Eq, Clone)] pub enum ParamStyle { Plain, Matrix, Query, Header, Template, } /// A WADL application. #[derive(Debug)] pub struct Application { /// Resources defined at the application level. pub resources: Vec, pub resource_types: Vec, /// Documentation for the application. pub docs: Vec, pub grammars: Vec, pub representations: Vec, } impl Application { pub fn get_resource_type_by_id(&self, id: &str) -> Option<&ResourceType> { self.resource_types.iter().find(|rt| id == rt.id.as_str()) } 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()) .chain( self.resource_types .iter() .flat_map(|rt| rt.iter_referenced_types()), ) } /// 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)] pub struct Resources { /// The base URL for the resources. pub base: Option, /// The resources defined at this level. pub resources: Vec, } #[derive(Debug)] pub struct Grammar { pub href: RiReferenceString, } #[derive(Debug, Clone, PartialEq, Eq)] pub enum ResourceTypeRef { Id(Id), Link(Url), 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 std::str::FromStr; use crate::ast::ResourceTypeRef::*; 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 { 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)] pub struct Options(HashMap>); impl std::hash::Hash for Options { fn hash(&self, state: &mut H) { let mut items = self.0.iter().collect::>(); items.sort(); for (key, value) in items { key.hash(state); value.hash(state); } } } impl Options { pub fn new() -> Self { Self(HashMap::new()) } pub fn len(&self) -> usize { self.0.len() } pub fn iter(&self) -> impl Iterator)> { self.0.iter().map(|(k, v)| (k.as_str(), v.as_ref())) } pub fn keys(&self) -> impl Iterator { self.0.keys().map(|k| k.as_str()) } pub fn is_empty(&self) -> bool { self.0.is_empty() } pub fn insert(&mut self, key: String, value: Option) { self.0.insert(key, value); } 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)] 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) -> impl Iterator { let mut params = self.params.iter().collect::>(); params.extend(self.subresources.iter().flat_map(|r| r.iter_all_params())); params.extend(self.methods.iter().flat_map(|m| m.iter_all_params())); params.into_iter() } /// 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.clone()) } } #[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)] pub struct Method { pub id: Id, pub name: String, pub docs: Vec, pub request: Request, 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)] 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 { pub fn new(content: String) -> Self { Self { content, ..Default::default() } } } #[derive(Debug, Clone, PartialEq, Eq)] pub struct 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, pub doc: Option, } #[derive(Debug, Clone, PartialEq, Eq)] pub struct Param { pub style: ParamStyle, pub id: Option, pub name: String, pub r#type: String, pub path: Option, pub required: bool, pub repeating: bool, pub fixed: Option, pub doc: Option, pub links: Vec, pub options: Option, } #[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct RepresentationDef { pub id: Option, pub media_type: Option, pub element: Option, pub profile: Option, pub docs: Vec, pub params: Vec, } impl RepresentationDef { fn iter_all_params(&self) -> impl Iterator { self.params.iter() } } #[derive(Debug, Clone)] pub enum RepresentationRef { /// A reference to a representation defined in the same document. Id(Id), Link(Url), } impl RepresentationRef { pub fn id(&self) -> Option<&str> { match self { RepresentationRef::Id(id) => Some(id), RepresentationRef::Link(l) => l.fragment(), } } } #[derive(Debug, Clone)] pub enum Representation { Reference(RepresentationRef), Definition(RepresentationDef), } impl Representation { pub fn media_type(&self) -> Option<&mime::Mime> { match self { Representation::Reference(_) => None, Representation::Definition(d) => d.media_type.as_ref(), } } 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), } } pub fn as_def(&self) -> Option<&RepresentationDef> { match self { Representation::Reference(_) => None, Representation::Definition(d) => Some(d), } } 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 { 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)] pub struct Request { pub docs: Vec, pub params: Vec, 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)] pub struct Response { pub docs: Vec, pub params: Vec, pub status: Option, 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)] pub struct ResourceType { pub id: Id, pub query_type: mime::Mime, pub methods: Vec, pub docs: Vec, pub subresources: Vec, pub params: Vec, } impl ResourceType { 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.clone()) } } wadl-0.3.0/src/codegen.rs000064400000000000000000001702511046102023000133220ustar 00000000000000use crate::ast::*; use std::collections::HashMap; /// MIME type for XHTML pub const XHTML_MIME_TYPE: &str = "application/xhtml+xml"; 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 } #[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"); } /// 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 it = name.chars().peekable(); let mut result = String::new(); let mut started = false; for c in it { if c.is_uppercase() { if !result.is_empty() && !started && !result.ends_with('_'){ result.push('_'); started = true; } result.push_str(c.to_lowercase().collect::().as_str()); } else { result.push(c); started = false; } } result } #[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"); } 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") } #[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); } /// 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() } #[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(), ] ); } 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); for doc in ¶m.doc { lines.extend(generate_doc(doc, 1, config)); } 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 !param.required { 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 param.required { 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()); } lines.push(format!(" {}fn set_{}(&mut self, value: {}) {{\n", if visibility.is_empty() { "".to_string() } else { format!("{} ", visibility) }, accessor_name, ret_type)); if param.required { 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 { 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 } pub fn resource_type_rust_type(r: &ResourceTypeRef) -> String { if let Some(id) = r.id() { camel_case_name(id) } else { "url::Url".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"); } 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()) } 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![]), 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())) { (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 !param.required { param_type = format!("Option<{}>", param_type); } (param_type, annotations) } #[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"); } 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), } } #[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"); } 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, } } #[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"); } 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| !x.required); 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 { 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)); for doc in ¶m.doc { 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 { 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 } #[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(), ] ); } fn supported_representation_def(_d: &RepresentationDef) -> bool { false } #[test] fn test_supported_representation_def() { let mut d = RepresentationDef::default(); d.media_type = Some(crate::WADL_MIME_TYPE.parse().unwrap()); 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)); } /// 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 pub 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[0].clone() } 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[0].clone() } else { format!("({})", ret.join(", ")) } } else { todo!( "multiple representations for response: {}: {:?}", name, representations ); } } #[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 mut 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()); } pub 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 } #[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() ] ); } fn apply_map_fn(map_fn: Option<&str>, ret: &str, required: bool) -> String { if let Some(map_fn) = map_fn { if required { if map_fn.starts_with('|') { format!("({})({})", map_fn, ret) } else { format!("{}({})", map_fn, ret) } } else { format!("{}.map({})", ret, map_fn) } } else { ret.to_string() } } #[test] fn test_apply_map_fn() { assert_eq!(apply_map_fn(None, "x", true), "x".to_string()); assert_eq!( apply_map_fn(Some("Some"), "x", true), "Some(x)".to_string() ); assert_eq!( apply_map_fn(Some("Some"), "x", false), "x.map(Some)".to_string() ); assert_eq!( apply_map_fn(Some("|y|y+1"), "x", true), "(|y|y+1)(x)".to_string() ); assert_eq!( apply_map_fn(Some("|y|y+1"), "x", false), "x.map(|y|y+1)".to_string() ); } pub 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 { lines.push("let mut form = reqwest::blocking::multipart::Form::new();\n".to_string()); 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!("reqwest::blocking::multipart::Part::text({})", 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 } pub 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 } pub 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); lines.push(format!(" pub fn {}_wadl<'a>(&self, client: &'a dyn wadl::Client) -> std::result::Result {{\n", 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(); 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()); 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 } pub 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)) { 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 wadl::Client", if visibility.is_empty() { "".to_string() } else { format!("{} ", visibility) }, 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(), ); 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)); } line.push_str(") -> std::result::Result<"); line.push_str(ret_type.as_str()); line.push_str(", 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(); 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()); 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, true)) } }; 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 param.required { 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), 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(Error::UnhandledContentType(resp)) }\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(" _ => Err(wadl::Error::UnhandledStatus(resp))\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 } #[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![ " pub fn foo<'a>(&self, client: &'a dyn wadl::Client) -> std::result::Result<(), 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(), " _ => Err(wadl::Error::UnhandledStatus(resp))\n".to_string(), " }\n".to_string(), " }\n".to_string(), "\n".to_string(), ]); } 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 { 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 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 } #[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 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(), ]); } #[derive(Default)] #[allow(clippy::type_complexity)] pub struct Config { /// 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 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>>, 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>>, } 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 } } #[cfg(test)] mod tests { use super::*; #[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"); } } pub 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 } 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 { lines.extend(generate_representation(representation, config, &options)); } for resource_type in &app.resource_types { 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)) } #[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()); } wadl-0.3.0/src/lib.rs000064400000000000000000000064671046102023000124730ustar 00000000000000//! # 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; } pub trait Client { 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) } } #[derive(Debug)] pub enum Error { InvalidUrl, 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::blocking::Response), /// The response content type was not handled by the library. UnhandledContentType(reqwest::blocking::Response), 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::UnhandledStatus(res) => write!(f, "Unhandled response. Code: {}, response type: {}", res.status(), res.headers().get("content-type").unwrap_or(&reqwest::header::HeaderValue::from_static("unknown")).to_str().unwrap_or("unknown")), Error::UnhandledContentType(res) => write!(f, "Unhandled response content type: {}", res.headers().get("content-type").unwrap_or(&reqwest::header::HeaderValue::from_static("unknown")).to_str().unwrap_or("unknown")), Error::Io(err) => write!(f, "IO error: {}", err), } } } impl std::error::Error for Error {} 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) } } 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()) } wadl-0.3.0/src/parse.rs000064400000000000000000000731601046102023000130310ustar 00000000000000use crate::ast::*; use std::io::Read; use iri_string::spec::IriSpec; use iri_string::types::{RiReferenceString}; use xmltree::Element; #[allow(unused)] pub const WADL_NS: &str = "http://wadl.dev.java.net/2009/02"; #[derive(Debug)] pub enum Error { Io(std::io::Error), Xml(xmltree::ParseError), Url(url::ParseError), 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#"