octocrab-0.31.2/.cargo_vcs_info.json0000644000000001360000000000100127130ustar { "git": { "sha1": "0bfd9dd890308dbb028f45d235334966cbe4c229" }, "path_in_vcs": "" }octocrab-0.31.2/.github/FUNDING.yml000064400000000000000000000001021046102023000146510ustar 00000000000000# These are supported funding model platforms github: XAMPPRocky octocrab-0.31.2/.github/dependabot.yml000064400000000000000000000002211046102023000156660ustar 00000000000000version: 2 updates: - package-ecosystem: cargo directory: "/" schedule: interval: daily time: "04:00" open-pull-requests-limit: 99 octocrab-0.31.2/.github/workflows/release-plz.yml000064400000000000000000000011221046102023000200420ustar 00000000000000name: Release-plz permissions: pull-requests: write contents: write on: push: branches: - main jobs: release-plz: name: Release-plz runs-on: ubuntu-22.04 steps: - name: Checkout repository uses: actions/checkout@v3 with: fetch-depth: 0 - name: Install Rust toolchain uses: dtolnay/rust-toolchain@stable - name: Run release-plz uses: MarcoIeni/release-plz-action@v0.5 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} octocrab-0.31.2/.github/workflows/rust.yml000064400000000000000000000010111046102023000166110ustar 00000000000000name: Rust on: push: pull_request: jobs: build: runs-on: ${{ matrix.os }}-latest strategy: matrix: channel: [stable, beta, nightly] os: [ubuntu, macos, windows] steps: - uses: actions/checkout@v2 - run: rustup default ${{ matrix.channel }} - run: cargo build --verbose --all-targets - run: cargo test clippy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - run: cargo fmt --all -- --check - run: cargo clippy --tests --examples octocrab-0.31.2/.gitignore000064400000000000000000000000321046102023000134660ustar 00000000000000/Cargo.lock /target .idea octocrab-0.31.2/CHANGELOG.md000064400000000000000000000276141046102023000133260ustar 00000000000000# Changelog All Octocrab releases are supported by the community and through [GitHub Sponsors][sp]. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] ## [0.31.2](https://github.com/XAMPPRocky/octocrab/compare/v0.31.1...v0.31.2) - 2023-10-15 ### Other - Add `follow-redirect` feature ([#469](https://github.com/XAMPPRocky/octocrab/pull/469)) ## [0.31.1](https://github.com/XAMPPRocky/octocrab/compare/v0.31.0...v0.31.1) - 2023-10-08 ### Other - add get_readme for RepoHandler ([#465](https://github.com/XAMPPRocky/octocrab/pull/465)) ## [0.31.0](https://github.com/XAMPPRocky/octocrab/compare/v0.30.1...v0.31.0) - 2023-10-02 ### Other - Add WatchEvent ([#462](https://github.com/XAMPPRocky/octocrab/pull/462)) - Checks API ([#461](https://github.com/XAMPPRocky/octocrab/pull/461)) - Fix `github_app_authentication.rs` ([#458](https://github.com/XAMPPRocky/octocrab/pull/458)) ## [0.30.1](https://github.com/XAMPPRocky/octocrab/compare/v0.30.0...v0.30.1) - 2023-09-04 ### Other - add tracing debug only when tracing feature is enabled ([#454](https://github.com/XAMPPRocky/octocrab/pull/454)) ## [0.30.0](https://github.com/XAMPPRocky/octocrab/compare/v0.29.3...v0.30.0) - 2023-09-01 ### Other - Fix commit_comment webhook event parsing ([#453](https://github.com/XAMPPRocky/octocrab/pull/453)) - Add Octocrab::users() and UsersHandler::repos ([#451](https://github.com/XAMPPRocky/octocrab/pull/451)) - Add CommitHandler::associated_check_runs ([#450](https://github.com/XAMPPRocky/octocrab/pull/450)) - Fix installation token cache issue ([#442](https://github.com/XAMPPRocky/octocrab/pull/442)) - Add projects Api ([#447](https://github.com/XAMPPRocky/octocrab/pull/447)) - Enhance installation webhook events - Add test for push webhook event - Implement custom deserialization for hybrid Github API timestamps - Make webhook_events deserialization tests more robust ## [0.29.3](https://github.com/XAMPPRocky/octocrab/compare/v0.29.2...v0.29.3) - 2023-08-15 ### Other - add `WebhookEventType::Schedule` variant ## [0.29.2](https://github.com/XAMPPRocky/octocrab/compare/v0.29.1...v0.29.2) - 2023-08-14 ### Fixed - fix get_asset url ### Other - Add optional email field to Author - Add get_org_installation to AppsRequestHandler - Update CHANGELOG.md ## [0.29.1](https://github.com/XAMPPRocky/octocrab/compare/v0.29.0...v0.29.1) - 2023-07-31 ### Other - Make models::webhook_events::payload public ## [0.29.0](https://github.com/XAMPPRocky/octocrab/compare/v0.28.0...v0.29.0) - 2023-07-30 ### Other - Add webhook event deserialization ([#427](https://github.com/XAMPPRocky/octocrab/pull/427)) - Update changelog for v0.28.0 ([#428](https://github.com/XAMPPRocky/octocrab/pull/428)) - Add associated pull requests and commit compare functionality ([#413](https://github.com/XAMPPRocky/octocrab/pull/413)) - Fix clippy 1.71 warnings ([#424](https://github.com/XAMPPRocky/octocrab/pull/424)) ## [0.28.0](https://github.com/XAMPPRocky/octocrab/compare/v0.27.0...v0.28.0) - 2023-07-19 ### Other - Handle errors when kicking off github workflows ([#409](https://github.com/XAMPPRocky/octocrab/pull/409)) - Update license field following https://doc.rust-lang.org/cargo/reference/manifest.html#the-license-and-license-file-fields ([#412](https://github.com/XAMPPRocky/octocrab/pull/416)) - cargo clippy --tests ([#416](https://github.com/XAMPPRocky/octocrab/pull/416)) - Improve workflow job types ([#414](https://github.com/XAMPPRocky/octocrab/pull/416)) - Fix graphql example ([#404](https://github.com/XAMPPRocky/octocrab/pull/404)) ## [0.27.0](https://github.com/XAMPPRocky/octocrab/compare/v0.26.0...v0.26.1) - 2023-07-18 ### Other - Handle errors when kicking off github workflows ([#409](https://github.com/XAMPPRocky/octocrab/pull/409)) - Update license field following https://doc.rust-lang.org/cargo/reference/manifest.html#the-license-and-license-file-fields ([#412](https://github.com/XAMPPRocky/octocrab/pull/412)) - cargo clippy --tests ([#416](https://github.com/XAMPPRocky/octocrab/pull/416)) - Improve workflow job types ([#414](https://github.com/XAMPPRocky/octocrab/pull/414)) - Fix graphql example ([#404](https://github.com/XAMPPRocky/octocrab/pull/404)) ## [0.25.1](https://github.com/XAMPPRocky/octocrab/compare/v0.25.0...v0.25.1) - 2023-06-06 ### Other - Pass through hyper-rustls/webpki-tokio ([#392](https://github.com/XAMPPRocky/octocrab/pull/392)) ## [0.25.0](https://github.com/XAMPPRocky/octocrab/compare/v0.24.0...v0.25.0) - 2023-06-03 ### Other - Add User Access Authentication ([#375](https://github.com/XAMPPRocky/octocrab/pull/375)) - Add allow_forking & allow_update_branch in Repository model ([#379](https://github.com/XAMPPRocky/octocrab/pull/379)) - added org secrets api ([#384](https://github.com/XAMPPRocky/octocrab/pull/384)) ## [0.24.0](https://github.com/XAMPPRocky/octocrab/compare/v0.23.0...v0.23.1) - 2023-06-02 ### Fixed - the API returns one reviewer not reviewers ([#390](https://github.com/XAMPPRocky/octocrab/pull/390)) - wrap pull_request_review_id in an Option ([#388](https://github.com/XAMPPRocky/octocrab/pull/388)) ### Other - Add Issue Timeline API ([#389](https://github.com/XAMPPRocky/octocrab/pull/389)) - add some of the missing fields to PullRequest ([#386](https://github.com/XAMPPRocky/octocrab/pull/386)) - Builder for list_reviews for pulls ([#387](https://github.com/XAMPPRocky/octocrab/pull/387)) - Link to `gists` documentation in README ([#383](https://github.com/XAMPPRocky/octocrab/pull/383)) ## [0.23.0](https://github.com/XAMPPRocky/octocrab/compare/v0.22.0...v0.22.1) - 2023-05-21 ### Other - Add "updated since" support to ListIssuesBuilder (#373) - Gists API: Complete support (#371) - Add more fields (#369) ## [0.22.0](https://github.com/XAMPPRocky/octocrab/compare/v0.21.0...v0.21.1) - 2023-05-16 ### Other - Add leading / to NotificationsHandler.lists() (#364) - Alter graphql method to pass arbitrarily complex payloads (variables and graphql-client support) (#332) - Fix authentication endpoints (#354) - Handle redirects for download_tarball (#359) - Make building without the `retry` feature work. (#358) - Add list_org_memberships_for_authenticated_user (#357) - add Uploader struct for Asset uploader field (#355) ## [0.21.0](https://github.com/XAMPPRocky/octocrab/compare/v0.20.0...v0.21.0) - 2023-04-29 ### Other - Add an example showing gist creation (#329) - Use CommitAuthor for Commit.author (#353) - Create release-plz.toml - Sort deps in cargo.toml (#352) - Enable rustls(and use as default client) (#351) - *(ci)* update release-plz version (#350) - Add missing pub to struct ListCheckRuns 😅 (#347) - Add Checks API skeleton (#345) - cargo fmt (#343) - Remove reqwest (#342) ## [0.20.0-alpha.3](https://github.com/XAMPPRocky/octocrab/compare/v0.20.0-alpha.2...v0.20.0-alpha.3) - 2023-04-12 ### Other - Handle `DELETE /gists/{gist_id}` (#333) ## [0.20.0-alpha.2](https://github.com/XAMPPRocky/octocrab/compare/v0.20.0-alpha.1...v0.20.0-alpha.2) - 2023-04-10 ### Other - Extend `GistsHandler` through `star(...)`, `unstar(...)`, `is_starred(...)` (#330) - added poll org events (#325) - Add `CurrentAuthHandler::list_gists_for_authenticated_user` (#328) - Fix typo in POST /gists endpoint (#327) - Update hyper-rustls requirement from 0.23.2 to 0.24.0 (#324) - Percent encode label name in remove_label to avoid InvalidUri(InvalidUriChar) error (#323) ## [0.20.0-alpha.1](https://github.com/XAMPPRocky/octocrab/compare/v0.20.0-alpha.0...v0.20.0-alpha.1) - 2023-03-31 ### Other - Fix GitHubError / InvalidUri(InvalidFormat) (#320) - Fix the spelling of `committer` in `RepoCommitPage` (#316) (#317) - Add update state reason (#290) - Add target URL to Status model (#308) - *(ci)* add release-plz (#309) - Add remove_requested_reviewer function (#312) - Make command compatible with copy paste (#318) - Update tower-http requirement from 0.3.2 to 0.4.0 (#315) ### Added Methods - [`UpdateIssueBuilder::state_reason`] Updates the state reason. ## 0.4.1 - Relaxed the `body` argument on `Octocrab::graphql` from `impl AsRef` to `&impl serde::Serialize + ?Sized` to allow accepting any valid JSON value. This is mainly useful for being able to use types from other libraries like [`graphql_client`][gql] directly. [gql]: https://docs.rs/graphql_client ## 0.4.0 ### New APIs - [`actions`] Control and automate GitHub Actions. - [`current`] Metadata about the currently authenticated user. - [`gitignore`] Get and generate gitignore templates. - [`licenses`] Metadata about licenses. - [`markdown`] Render markdown with GitHub. - [`orgs`] Organisations - [`pulls`] Pull Requests - [`repos`] Repositories - [`search`] Search using GitHub's query syntax. ### Added Methods - [`Octocrab::graphql`][`graphql`] Send a GraphQL request. - [`IssueHandler::lock`] Lock a GitHub issue with an optional reason. - [`IssueHandler::unlock`] Unlock a GitHub issue. - [`IssueHandler::replace_all_labels`] Replace all labels on an issue. - [`IssueHandler::delete_label`] Remove labels from an issue. - [`IssueHandler::list_labels_for_issue`] List all labels on an issue. - [`IssueHandler::list_labels_for_repo`] List all labels in a repository. - [`PullRequestHandler::media_type`] Set the media type for a single request. - [`PullRequestHandler::get_diff`] Get a pull request's diff file. - [`PullRequestHandler::get_patch`] Get a pull request's patch file. - [`Page::number_of_pages`] Get the number of pages in a paginated query if possible. ### Changes - [`Page`] now has new fields for being used with GitHub's search APi such as `incomplete_results` and `total_count`. [`actions`]: https://docs.rs/octocrab/0.4.1/octocrab/actions/struct.ActionsHandler.html [`current`]: https://docs.rs/octocrab/0.4.1/octocrab/current/struct.CurrentAuthHandler.html [`gitignore`]: https://docs.rs/octocrab/0.4.1/octocrab/gitignore/struct.GitignoreHandler.html [`graphql`]: https://docs.rs/octocrab/0.4.1/octocrab/struct.Octocrab.html#graphql-api [`markdown`]: https://docs.rs/octocrab/0.4.1/octocrab/gitignore/struct.MarkdownHandler.html [`issues`]: https://docs.rs/octocrab/0.4.1/octocrab/issues/struct.IssueHandler.html [`licenses`]: https://docs.rs/octocrab/0.4.1/octocrab/licenses/struct.LicenseHandler.html [`pulls`]: https://docs.rs/octocrab/0.4.1/octocrab/pulls/struct.PullRequestHandler.html [`orgs`]: https://docs.rs/octocrab/0.4.1/octocrab/orgs/struct.OrgHandler.html [`repos`]: https://docs.rs/octocrab/0.4.1/octocrab/repos/struct.RepoHandler.html [`search`]: https://docs.rs/octocrab/0.4.1/octocrab/search/struct.SearchHandler.html [`teams`]: https://docs.rs/octocrab/0.4.1/octocrab/teams/struct.TeamHandler.html [sp]: https://github.com/sponsors/XAMPPRocky [`IssueHandler::lock`]: https://docs.rs/octocrab/0.4.1/octocrab/issues/struct.IssueHandler.html#method.lock [`IssueHandler::unlock`]: https://docs.rs/octocrab/0.4.1/octocrab/issues/struct.IssueHandler.html#method.unlock [`IssueHandler::replace_all_labels`]: https://docs.rs/octocrab/0.4.1/octocrab/issues/struct.IssueHandler.html#method.replace_all_labels [`IssueHandler::delete_label`]: https://docs.rs/octocrab/0.4.1/octocrab/issues/struct.IssueHandler.html#method.delete_label [`IssueHandler::list_labels_for_issue`]: https://docs.rs/octocrab/0.4.1/octocrab/issues/struct.IssueHandler.html#method.list_labels_for_issue [`IssueHandler::list_labels_for_repo`]: https://docs.rs/octocrab/0.4.1/octocrab/issues/struct.IssueHandler.html#method.list_labels_for_repo [`PullRequestHandler::media_type`]: https://docs.rs/octocrab/0.4.1/octocrab/pulls/struct.PullRequestHandler.html#method.media_type [`PullRequestHandler::get_diff`]: https://docs.rs/octocrab/0.4.1/octocrab/pulls/struct.PullRequestHandler.html#method.get_diff [`PullRequestHandler::get_patch`]: https://docs.rs/octocrab/0.4.1/octocrab/pulls/struct.PullRequestHandler.html#method.get_patch [`Page`]: https://docs.rs/octocrab/0.4.1/octocrab/struct.Page.html [`Page::number_of_pages`]: https://docs.rs/octocrab/0.4.1/octocrab/struct.Page.html#method.number_of_pages octocrab-0.31.2/Cargo.lock0000644000001551240000000000100106760ustar # 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 = "aead" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ "crypto-common", "generic-array", ] [[package]] name = "aho-corasick" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] [[package]] name = "android-tzdata" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" [[package]] name = "android_system_properties" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" dependencies = [ "libc", ] [[package]] name = "anyhow" version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "arc-swap" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" [[package]] name = "assert-json-diff" version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12" dependencies = [ "serde", "serde_json", ] [[package]] name = "async-channel" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" dependencies = [ "concurrent-queue", "event-listener", "futures-core", ] [[package]] name = "async-stream" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" dependencies = [ "async-stream-impl", "futures-core", "pin-project-lite", ] [[package]] name = "async-stream-impl" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", "syn 2.0.38", ] [[package]] name = "async-trait" version = "0.1.74" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" dependencies = [ "proc-macro2", "quote", "syn 2.0.38", ] [[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.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" version = "0.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" [[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.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" [[package]] name = "blake2" version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" dependencies = [ "digest 0.10.7", ] [[package]] name = "block-buffer" version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array", ] [[package]] name = "bumpalo" version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[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 = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chacha20" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" dependencies = [ "cfg-if", "cipher", "cpufeatures", ] [[package]] name = "chacha20poly1305" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" dependencies = [ "aead", "chacha20", "cipher", "poly1305", "zeroize", ] [[package]] name = "chrono" version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" dependencies = [ "android-tzdata", "iana-time-zone", "num-traits", "serde", "windows-targets", ] [[package]] name = "cipher" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ "crypto-common", "inout", "zeroize", ] [[package]] name = "concurrent-queue" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f057a694a54f12365049b0958a1685bb52d567f5593b355fbf685838e873d400" dependencies = [ "crossbeam-utils", ] [[package]] name = "core-foundation" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" dependencies = [ "core-foundation-sys", "libc", ] [[package]] name = "core-foundation-sys" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "cpufeatures" version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" dependencies = [ "libc", ] [[package]] name = "crossbeam-utils" version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ "cfg-if", ] [[package]] name = "crypto-common" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", "rand_core 0.6.4", "typenum", ] [[package]] name = "crypto_box" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd26c32de5307fd08aac445a75c43472b14559d5dccdfba8022dbcd075838ebc" dependencies = [ "aead", "blake2", "chacha20", "chacha20poly1305", "salsa20", "x25519-dalek", "xsalsa20poly1305", "zeroize", ] [[package]] name = "curve25519-dalek" version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" dependencies = [ "byteorder", "digest 0.9.0", "rand_core 0.5.1", "subtle", "zeroize", ] [[package]] name = "deadpool" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "421fe0f90f2ab22016f32a9881be5134fdd71c65298917084b0c7477cbc3856e" dependencies = [ "async-trait", "deadpool-runtime", "num_cpus", "retain_mut", "tokio", ] [[package]] name = "deadpool-runtime" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63dfa964fe2a66f3fde91fc70b267fe193d822c7e603e2a675a49a7f46ad3f49" [[package]] name = "deranged" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" dependencies = [ "powerfmt", ] [[package]] name = "diff" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" [[package]] name = "digest" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ "generic-array", ] [[package]] name = "digest" version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", "subtle", ] [[package]] name = "doc-comment" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "either" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "errno" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" dependencies = [ "libc", "windows-sys", ] [[package]] name = "event-listener" version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "fastrand" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" dependencies = [ "instant", ] [[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.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ "percent-encoding", ] [[package]] name = "futures" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" dependencies = [ "futures-channel", "futures-core", "futures-executor", "futures-io", "futures-sink", "futures-task", "futures-util", ] [[package]] name = "futures-channel" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", "futures-sink", ] [[package]] name = "futures-core" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-executor" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" dependencies = [ "futures-core", "futures-task", "futures-util", ] [[package]] name = "futures-io" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" [[package]] name = "futures-lite" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" dependencies = [ "fastrand 1.9.0", "futures-core", "futures-io", "memchr", "parking", "pin-project-lite", "waker-fn", ] [[package]] name = "futures-macro" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", "syn 2.0.38", ] [[package]] name = "futures-sink" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-timer" version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" [[package]] name = "futures-util" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-channel", "futures-core", "futures-io", "futures-macro", "futures-sink", "futures-task", "memchr", "pin-project-lite", "pin-utils", "slab", ] [[package]] name = "generic-array" version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", ] [[package]] name = "getrandom" version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ "cfg-if", "libc", "wasi 0.9.0+wasi-snapshot-preview1", ] [[package]] name = "getrandom" version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", "libc", "wasi 0.11.0+wasi-snapshot-preview1", ] [[package]] name = "gimli" version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" [[package]] name = "h2" version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" dependencies = [ "bytes", "fnv", "futures-core", "futures-sink", "futures-util", "http", "indexmap", "slab", "tokio", "tokio-util", "tracing", ] [[package]] name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[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 = "http" version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ "bytes", "fnv", "itoa", ] [[package]] name = "http-body" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ "bytes", "http", "pin-project-lite", ] [[package]] name = "http-range-header" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" [[package]] name = "http-types" version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e9b187a72d63adbfba487f48095306ac823049cb504ee195541e91c7775f5ad" dependencies = [ "anyhow", "async-channel", "base64 0.13.1", "futures-lite", "http", "infer", "pin-project-lite", "rand", "serde", "serde_json", "serde_qs", "serde_urlencoded", "url", ] [[package]] name = "httparse" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ "bytes", "futures-channel", "futures-core", "futures-util", "h2", "http", "http-body", "httparse", "httpdate", "itoa", "pin-project-lite", "socket2 0.4.9", "tokio", "tower-service", "tracing", "want", ] [[package]] name = "hyper-rustls" version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" dependencies = [ "futures-util", "http", "hyper", "log", "rustls", "rustls-native-certs", "tokio", "tokio-rustls", "webpki-roots", ] [[package]] name = "hyper-timeout" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" dependencies = [ "hyper", "pin-project-lite", "tokio", "tokio-io-timeout", ] [[package]] name = "hyper-tls" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ "bytes", "hyper", "native-tls", "tokio", "tokio-native-tls", ] [[package]] name = "iana-time-zone" version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", "windows", ] [[package]] name = "iana-time-zone-haiku" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ "cc", ] [[package]] name = "idna" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ "unicode-bidi", "unicode-normalization", ] [[package]] name = "indexmap" version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown", ] [[package]] name = "infer" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64e9829a50b42bb782c1df523f78d332fe371b10c661e78b7a3c34b0198e9fac" [[package]] name = "inout" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" dependencies = [ "generic-array", ] [[package]] name = "instant" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if", ] [[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 = "itoa" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "js-sys" version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] [[package]] name = "jsonwebtoken" version = "8.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" dependencies = [ "base64 0.21.4", "pem", "ring", "serde", "serde_json", "simple_asn1", ] [[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.149" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" [[package]] name = "linux-raw-sys" version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" [[package]] name = "log" version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "memchr" version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "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.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys", ] [[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 = "num-bigint" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" dependencies = [ "autocfg", "num-integer", "num-traits", ] [[package]] name = "num-integer" version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ "autocfg", "num-traits", ] [[package]] name = "num-traits" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", ] [[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 = "octocrab" version = "0.31.2" dependencies = [ "arc-swap", "async-trait", "base64 0.21.4", "bytes", "cfg-if", "chrono", "crypto_box", "either", "futures", "futures-core", "futures-util", "http", "http-body", "hyper", "hyper-rustls", "hyper-timeout", "hyper-tls", "jsonwebtoken", "once_cell", "percent-encoding", "pin-project", "pretty_assertions", "secrecy", "serde", "serde_json", "serde_path_to_error", "serde_urlencoded", "snafu", "tokio", "tokio-test", "tower", "tower-http", "tracing", "url", "wiremock", ] [[package]] name = "once_cell" version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "opaque-debug" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openssl" version = "0.10.57" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c" dependencies = [ "bitflags 2.4.0", "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.38", ] [[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.93" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d" dependencies = [ "cc", "libc", "pkg-config", "vcpkg", ] [[package]] name = "parking" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e52c774a4c39359c1d1c52e43f73dd91a75a614652c825408eec30c95a9b2067" [[package]] name = "pem" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" dependencies = [ "base64 0.13.1", ] [[package]] name = "percent-encoding" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pin-project" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", "syn 2.0.38", ] [[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 = "poly1305" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" dependencies = [ "cpufeatures", "opaque-debug", "universal-hash", ] [[package]] name = "powerfmt" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "pretty_assertions" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" dependencies = [ "diff", "yansi", ] [[package]] name = "proc-macro2" version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] [[package]] name = "rand" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ "getrandom 0.1.16", "libc", "rand_chacha", "rand_core 0.5.1", "rand_hc", ] [[package]] name = "rand_chacha" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" dependencies = [ "ppv-lite86", "rand_core 0.5.1", ] [[package]] name = "rand_core" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" dependencies = [ "getrandom 0.1.16", ] [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom 0.2.10", ] [[package]] name = "rand_hc" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" dependencies = [ "rand_core 0.5.1", ] [[package]] name = "redox_syscall" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ "bitflags 1.3.2", ] [[package]] name = "regex" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aaac441002f822bc9705a681810a4dd2963094b9ca0ddc41cb963a4c189189ea" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5011c7e263a695dc8ca064cddb722af1be54e517a280b12a5356f98366899e5d" 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 = "retain_mut" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4389f1d5789befaf6029ebd9f7dac4af7f7e3d61b69d4f30e2ac02b57e7712b0" [[package]] name = "ring" version = "0.16.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" dependencies = [ "cc", "libc", "once_cell", "spin", "untrusted", "web-sys", "winapi", ] [[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.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "745ecfa778e66b2b63c88a61cb36e0eea109e803b0b86bf9879fbc77c70e86ed" dependencies = [ "bitflags 2.4.0", "errno", "libc", "linux-raw-sys", "windows-sys", ] [[package]] name = "rustls" version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" dependencies = [ "log", "ring", "rustls-webpki 0.101.6", "sct", ] [[package]] name = "rustls-native-certs" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" dependencies = [ "openssl-probe", "rustls-pemfile", "schannel", "security-framework", ] [[package]] name = "rustls-pemfile" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" dependencies = [ "base64 0.21.4", ] [[package]] name = "rustls-webpki" version = "0.100.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f6a5fc258f1c1276dfe3016516945546e2d5383911efc0fc4f1cdc5df3a4ae3" dependencies = [ "ring", "untrusted", ] [[package]] name = "rustls-webpki" version = "0.101.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c7d5dece342910d9ba34d259310cae3e0154b873b35408b787b59bce53d34fe" dependencies = [ "ring", "untrusted", ] [[package]] name = "ryu" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "salsa20" version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" dependencies = [ "cipher", ] [[package]] name = "schannel" version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ "windows-sys", ] [[package]] name = "sct" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" dependencies = [ "ring", "untrusted", ] [[package]] name = "secrecy" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" dependencies = [ "zeroize", ] [[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.189" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" dependencies = [ "proc-macro2", "quote", "syn 2.0.38", ] [[package]] name = "serde_json" version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" dependencies = [ "itoa", "ryu", "serde", ] [[package]] name = "serde_path_to_error" version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4beec8bce849d58d06238cb50db2e1c417cfeafa4c63f692b15c82b7c80f8335" dependencies = [ "itoa", "serde", ] [[package]] name = "serde_qs" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7715380eec75f029a4ef7de39a9200e0a63823176b759d055b613f5a87df6a6" dependencies = [ "percent-encoding", "serde", "thiserror", ] [[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 = "simple_asn1" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" dependencies = [ "num-bigint", "num-traits", "thiserror", "time", ] [[package]] name = "slab" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] [[package]] name = "snafu" version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4de37ad025c587a29e8f3f5605c00f70b98715ef90b9061a815b9e59e9042d6" dependencies = [ "backtrace", "doc-comment", "snafu-derive", ] [[package]] name = "snafu-derive" version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "990079665f075b699031e9c08fd3ab99be5029b96f3b78dc0709e8f77e4efebf" dependencies = [ "heck", "proc-macro2", "quote", "syn 1.0.109", ] [[package]] name = "socket2" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", "winapi", ] [[package]] name = "socket2" version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" dependencies = [ "libc", "windows-sys", ] [[package]] name = "spin" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "subtle" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[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.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "tempfile" version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" dependencies = [ "cfg-if", "fastrand 2.0.1", "redox_syscall", "rustix", "windows-sys", ] [[package]] name = "thiserror" version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" dependencies = [ "proc-macro2", "quote", "syn 2.0.38", ] [[package]] name = "time" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" dependencies = [ "deranged", "itoa", "powerfmt", "serde", "time-core", "time-macros", ] [[package]] name = "time-core" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" dependencies = [ "time-core", ] [[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.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653" dependencies = [ "backtrace", "bytes", "libc", "mio", "num_cpus", "pin-project-lite", "socket2 0.5.4", "tokio-macros", "windows-sys", ] [[package]] name = "tokio-io-timeout" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" dependencies = [ "pin-project-lite", "tokio", ] [[package]] name = "tokio-macros" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", "syn 2.0.38", ] [[package]] name = "tokio-native-tls" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" dependencies = [ "native-tls", "tokio", ] [[package]] name = "tokio-rustls" version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ "rustls", "tokio", ] [[package]] name = "tokio-stream" version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" dependencies = [ "futures-core", "pin-project-lite", "tokio", ] [[package]] name = "tokio-test" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89b3cbabd3ae862100094ae433e1def582cf86451b4e9bf83aa7ac1d8a7d719" dependencies = [ "async-stream", "bytes", "futures-core", "tokio", "tokio-stream", ] [[package]] name = "tokio-util" version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d68074620f57a0b21594d9735eb2e98ab38b17f80d3fcb189fca266771ca60d" 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", "tokio-util", "tower-layer", "tower-service", "tracing", ] [[package]] name = "tower-http" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140" dependencies = [ "bitflags 2.4.0", "bytes", "futures-core", "futures-util", "http", "http-body", "http-range-header", "iri-string", "pin-project-lite", "tower", "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.39" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee2ef2af84856a50c1d430afce2fdded0a4ec7eda868db86409b4543df0797f9" dependencies = [ "log", "pin-project-lite", "tracing-attributes", "tracing-core", ] [[package]] name = "tracing-attributes" version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", "syn 2.0.38", ] [[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.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "typenum" version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-bidi" version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" dependencies = [ "tinyvec", ] [[package]] name = "universal-hash" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" dependencies = [ "crypto-common", "subtle", ] [[package]] name = "untrusted" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "url" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" dependencies = [ "form_urlencoded", "idna", "percent-encoding", "serde", ] [[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 = "waker-fn" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" [[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.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" [[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.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", "syn 2.0.38", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", "syn 2.0.38", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "web-sys" version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] name = "webpki-roots" version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b03058f88386e5ff5310d9111d53f48b17d732b401aeb83a8d5190f2ac459338" dependencies = [ "rustls-webpki 0.100.3", ] [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ "windows-targets", ] [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "wiremock" version = "0.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6f71803d3a1c80377a06221e0530be02035d5b3e854af56c6ece7ac20ac441d" dependencies = [ "assert-json-diff", "async-trait", "base64 0.21.4", "deadpool", "futures", "futures-timer", "http-types", "hyper", "log", "once_cell", "regex", "serde", "serde_json", "tokio", ] [[package]] name = "x25519-dalek" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a0c105152107e3b96f6a00a65e86ce82d9b125230e1c4302940eca58ff71f4f" dependencies = [ "curve25519-dalek", "rand_core 0.5.1", "zeroize", ] [[package]] name = "xsalsa20poly1305" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02a6dad357567f81cd78ee75f7c61f1b30bb2fe4390be8fb7c69e2ac8dffb6c7" dependencies = [ "aead", "poly1305", "salsa20", "subtle", "zeroize", ] [[package]] name = "yansi" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" [[package]] name = "zeroize" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" dependencies = [ "zeroize_derive", ] [[package]] name = "zeroize_derive" version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", "syn 2.0.38", ] octocrab-0.31.2/Cargo.toml0000644000000071550000000000100107210ustar # 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 = "2018" name = "octocrab" version = "0.31.2" authors = ["XAMPPRocky "] description = "A modern, extensible GitHub API client." homepage = "https://github.com/XAMPPRocky/octocrab" documentation = "https://docs.rs/octocrab" readme = "README.md" keywords = [ "github", "github-api", ] categories = ["web-programming::http-client"] license = "MIT OR Apache-2.0" repository = "https://github.com/XAMPPRocky/octocrab.git" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies.arc-swap] version = "1.3.0" [dependencies.async-trait] version = "0.1.50" [dependencies.base64] version = "0.21.0" [dependencies.bytes] version = "1.0.1" [dependencies.cfg-if] version = "1.0.0" [dependencies.chrono] version = "0.4.19" features = [ "serde", "clock", ] default-features = false [dependencies.either] version = "1.8.0" [dependencies.futures] version = "0.3.15" [dependencies.futures-core] version = "0.3.15" optional = true [dependencies.futures-util] version = "0.3.15" optional = true [dependencies.http] version = "0.2.5" [dependencies.http-body] version = "0.4.5" [dependencies.hyper] version = "0.14.13" features = [ "client", "http1", "stream", "tcp", ] [dependencies.hyper-rustls] version = "0.24.0" optional = true [dependencies.hyper-timeout] version = "0.4.1" optional = true [dependencies.hyper-tls] version = "0.5.0" optional = true [dependencies.jsonwebtoken] version = "8" [dependencies.once_cell] version = "1.7.2" [dependencies.percent-encoding] version = "2.2.0" [dependencies.pin-project] version = "1.0.12" [dependencies.secrecy] version = "0.8.0" [dependencies.serde] version = "1.0.126" features = ["derive"] [dependencies.serde_json] version = "1.0.64" [dependencies.serde_path_to_error] version = "0.1.4" [dependencies.serde_urlencoded] version = "0.7.1" [dependencies.snafu] version = "0.7" features = ["backtraces"] [dependencies.tokio] version = "1.17.0" optional = true default-features = false [dependencies.tower] version = "0.4.13" features = [ "util", "buffer", ] default-features = false [dependencies.tower-http] version = "0.4.0" features = [ "map-response-body", "trace", ] [dependencies.tracing] version = "0.1.37" features = ["log"] optional = true [dependencies.url] version = "2.2.2" features = ["serde"] [dev-dependencies.base64] version = "0.21.2" [dev-dependencies.crypto_box] version = "0.8.2" features = ["seal"] [dev-dependencies.pretty_assertions] version = "1.4.0" [dev-dependencies.tokio] version = "1.17.0" features = [ "macros", "rt-multi-thread", "time", ] default-features = false [dev-dependencies.tokio-test] version = "0.4.2" [dev-dependencies.wiremock] version = "0.5.3" [features] default = [ "follow-redirect", "retry", "rustls", "timeout", "tracing", ] follow-redirect = ["tower-http/follow-redirect"] opentls = ["hyper-tls"] retry = [ "tower/retry", "futures-util", ] rustls = ["hyper-rustls"] rustls-webpki-tokio = ["hyper-rustls/webpki-tokio"] stream = [ "futures-core", "futures-util", "hyper/stream", ] timeout = [ "hyper-timeout", "tokio", "tower/timeout", ] octocrab-0.31.2/Cargo.toml.orig000064400000000000000000000050371046102023000143770ustar 00000000000000[package] name = "octocrab" version = "0.31.2" authors = ["XAMPPRocky "] edition = "2018" readme = "README.md" homepage = "https://github.com/XAMPPRocky/octocrab" repository = "https://github.com/XAMPPRocky/octocrab.git" description = "A modern, extensible GitHub API client." license = "MIT OR Apache-2.0" documentation = "https://docs.rs/octocrab" categories = ["web-programming::http-client"] keywords = ["github", "github-api"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] async-trait = "0.1.50" arc-swap = "1.3.0" base64 = "0.21.0" bytes = "1.0.1" chrono = { version = "0.4.19", default-features = false, features = [ "serde", "clock", ] } cfg-if = "1.0.0" either = "1.8.0" futures = { version = "0.3.15" } futures-core = { version = "0.3.15", optional = true } futures-util = { version = "0.3.15", optional = true } jsonwebtoken = "8" http = "0.2.5" http-body = "0.4.5" hyper = { version = "0.14.13", features = ["client", "http1", "stream", "tcp"] } hyper-rustls = { version = "0.24.0", optional = true } hyper-timeout = { version = "0.4.1", optional = true } hyper-tls = { version = "0.5.0", optional = true } once_cell = "1.7.2" percent-encoding = "2.2.0" pin-project = "1.0.12" secrecy = "0.8.0" serde = { version = "1.0.126", features = ["derive"] } serde_json = "1.0.64" serde_path_to_error = "0.1.4" serde_urlencoded = "0.7.1" snafu = { version = "0.7", features = ["backtraces"] } tokio = { version = "1.17.0", default-features = false, optional = true } tower = { version = "0.4.13", default-features = false, features = ["util", "buffer"] } tower-http = { version = "0.4.0", features = ["map-response-body", "trace"] } tracing = { version = "0.1.37", features = ["log"], optional = true } url = { version = "2.2.2", features = ["serde"] } [dev-dependencies] tokio = { version = "1.17.0", default-features = false, features = [ "macros", "rt-multi-thread", "time", ] } tokio-test = "0.4.2" wiremock = "0.5.3" crypto_box = { version = "0.8.2", features = ["seal"] } base64 = "0.21.2" pretty_assertions = "1.4.0" [features] default = ["follow-redirect", "retry", "rustls", "timeout", "tracing"] follow-redirect = ["tower-http/follow-redirect"] retry = ["tower/retry", "futures-util"] rustls = ["hyper-rustls"] rustls-webpki-tokio = ["hyper-rustls/webpki-tokio"] opentls = ["hyper-tls"] stream = ["futures-core", "futures-util", "hyper/stream"] timeout = ["hyper-timeout", "tokio", "tower/timeout"] octocrab-0.31.2/LICENCE-APACHE000064400000000000000000000010471046102023000134110ustar 00000000000000Copyright 2016 Erin Power Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. octocrab-0.31.2/LICENCE-MIT000064400000000000000000000020611046102023000131160ustar 00000000000000MIT License (MIT) Copyright (c) 2016 Erin Power Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. octocrab-0.31.2/README.md000064400000000000000000000167341046102023000127750ustar 00000000000000# Octocrab: A modern, extensible GitHub API client. [![Rust](https://github.com/XAMPPRocky/octocrab/workflows/Rust/badge.svg)](https://github.com/XAMPPRocky/octocrab/actions?query=workflow%3ARust) [![crates.io](https://img.shields.io/crates/d/octocrab.svg)](https://crates.io/crates/octocrab) [![Help Wanted](https://img.shields.io/github/issues/XAMPPRocky/octocrab/help%20wanted?color=green)](https://github.com/XAMPPRocky/octocrab/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) [![Lines Of Code](https://tokei.rs/b1/github/XAMPPRocky/octocrab?category=code)](https://github.com/XAMPPRocky/octocrab) [![Documentation](https://docs.rs/octocrab/badge.svg)](https://docs.rs/octocrab/) [![Crates.io](https://img.shields.io/crates/v/octocrab?logo=rust)](https://crates.io/crates/octocrab/) Octocrab is a third party GitHub API client, allowing you to easily build your own GitHub integrations or bots in Rust. `Octocrab` comes with two primary sets of APIs for communicating with GitHub, a high level strongly typed semantic API, and a lower level HTTP API for extending behaviour. ## Adding Octocrab Run this command in your terminal to add the latest version of `Octocrab`. ```bash cargo add octocrab ``` ## Semantic API The semantic API provides strong typing around GitHub's API, a set of [`models`] that maps to GitHub's types, and [`auth`] functions that are useful for GitHub apps. Currently, the following modules are available as of version `0.17`. - [`actions`] GitHub Actions. - [`apps`] GitHub Apps. - [`current`] Information about the current user. - [`gitignore`] Gitignore templates. - [`graphql`] GraphQL. - [`issues`] Issues and related items, e.g. comments, labels, etc. - [`licenses`] License Metadata. - [`markdown`] Rendering Markdown with GitHub. - [`orgs`] GitHub Organisations. - [`pulls`] Pull Requests. - [`releases`] Releases. - [`repos`] Repositories. - [`search`] GitHub's search API. - [`teams`] Teams. - [`gists`] GitHub's gists API - [`users`] Users. [`models`]: https://docs.rs/octocrab/latest/octocrab/models/index.html [`auth`]: https://docs.rs/octocrab/latest/octocrab/auth/index.html [`apps`]: https://docs.rs/octocrab/latest/octocrab/apps/index.html [`actions`]: https://docs.rs/octocrab/latest/octocrab/actions/struct.ActionsHandler.html [`current`]: https://docs.rs/octocrab/latest/octocrab/current/struct.CurrentAuthHandler.html [`gitignore`]: https://docs.rs/octocrab/latest/octocrab/gitignore/struct.GitignoreHandler.html [`graphql`]: https://docs.rs/octocrab/latest/octocrab/struct.Octocrab.html#graphql-api [`markdown`]: https://docs.rs/octocrab/latest/octocrab/markdown/struct.MarkdownHandler.html [`issues`]: https://docs.rs/octocrab/latest/octocrab/issues/struct.IssueHandler.html [`licenses`]: https://docs.rs/octocrab/latest/octocrab/licenses/struct.LicenseHandler.html [`pulls`]: https://docs.rs/octocrab/latest/octocrab/pulls/struct.PullRequestHandler.html [`orgs`]: https://docs.rs/octocrab/latest/octocrab/orgs/struct.OrgHandler.html [`repos`]: https://docs.rs/octocrab/latest/octocrab/repos/struct.RepoHandler.html [`releases`]: https://docs.rs/octocrab/latest/octocrab/repos/struct.ReleasesHandler.html [`search`]: https://docs.rs/octocrab/latest/octocrab/search/struct.SearchHandler.html [`teams`]: https://docs.rs/octocrab/latest/octocrab/teams/struct.TeamHandler.html [`gists`]: https://docs.rs/octocrab/latest/octocrab/gists/struct.GistsHandler.html [`users`]: https://docs.rs/octocrab/latest/octocrab/gists/struct.UsersHandler.html #### Getting a Pull Request ```rust // Get pull request #5 from `XAMPPRocky/octocrab`. let issue = octocrab::instance().pulls("XAMPPRocky", "octocrab").get(5).await?; ``` All methods with multiple optional parameters are built as `Builder` structs, allowing you to easily specify parameters. #### Listing issues ```rust let octocrab = octocrab::instance(); // Returns the first page of all issues. let mut page = octocrab .issues("XAMPPRocky", "octocrab") .list() // Optional Parameters .creator("XAMPPRocky") .state(params::State::All) .per_page(50) .send() .await?; // Go through every page of issues. Warning: There's no rate limiting so // be careful. loop { for issue in &page { println!("{}", issue.title); } page = match octocrab .get_page::(&page.next) .await? { Some(next_page) => next_page, None => break, } } ``` ## HTTP API The typed API currently doesn't cover all of GitHub's API at this time, and even if it did GitHub is in active development and this library will likely always be somewhat behind GitHub at some points in time. However that shouldn't mean that in order to use those features, you have to fork or replace `octocrab` with your own solution. Instead `octocrab` exposes a suite of HTTP methods allowing you to easily extend `Octocrab`'s existing behaviour. Using these HTTP methods allows you to keep using the same authentication and configuration, while having control over the request and response. There is a method for each HTTP method, `get`, `post`, `patch`, `put`, `delete`, all of which accept a relative route and a optional body. ```rust let user: octocrab::models::User = octocrab::instance() .get("/user", None::<&()>) .await?; ``` Each of the HTTP methods expects a body, formats the URL with the base URL, and errors if GitHub doesn't return a successful status, but this isn't always desired when working with GitHub's API, sometimes you need to check the response status or headers. As such there are companion methods `_get`, `_post`, etc. that perform no additional pre or post-processing to the request. ```rust let octocrab = octocrab::instance(); let response = octocrab ._get("https://api.github.com/organizations") .await?; // You can also use `Uri::builder().authority("").path_and_query("")` if you want to customize the base uri and path. let response = octocrab ._get(Uri::builder().path_and_query("/organizations").build().expect("valid uri")) .await?; ``` You can use the those HTTP methods to easily create your own extensions to `Octocrab`'s typed API. (Requires `async_trait`). ```rust use octocrab::{Octocrab, Page, Result, models}; #[async_trait::async_trait] trait OrganisationExt { async fn list_every_organisation(&self) -> Result>; } #[async_trait::async_trait] impl OrganisationExt for Octocrab { async fn list_every_organisation(&self) -> Result> { self.get("/organizations", None::<&()>).await } } ``` You can also easily access new properties that aren't available in the current models using `serde`. ```rust #[derive(Deserialize)] struct RepositoryWithVisibility { #[serde(flatten)] inner: octocrab::models::Repository, visibility: String, } let my_repo = octocrab::instance() .get::("https://api.github.com/repos/XAMPPRocky/octocrab", None::<&()>) .await?; ``` ## Static API `Octocrab` also provides a statically reference counted version of its API, allowing you to easily plug it into existing systems without worrying about having to integrate and pass around the client. ```rust // Initialises the static instance with your configuration and returns an // instance of the client. octocrab::initialise(octocrab::Octocrab::builder()); // Gets a instance of `Octocrab` from the static API. If you call this // without first calling `octocrab::initialise` a default client will be // initialised and returned instead. let octocrab = octocrab::instance(); ``` octocrab-0.31.2/examples/create_a_gist.rs000064400000000000000000000012751046102023000164650ustar 00000000000000use octocrab::Octocrab; #[tokio::main] async fn main() -> octocrab::Result<()> { let token = std::env::var("GITHUB_TOKEN").expect("GITHUB_TOKEN env variable is required"); let octocrab = Octocrab::builder().personal_token(token).build()?; println!("Creating a gist with hello world in rust on your account"); let gist = octocrab .gists() .create() .file( "hello_world.rs", "fn main() {\n println!(\"Hello World!\");\n}", ) // Optional Parameters .description("Hello World in Rust") .public(false) .send() .await?; println!("Done, created: {url}", url = gist.html_url); Ok(()) } octocrab-0.31.2/examples/create_org_secret.rs000064400000000000000000000024531046102023000173520ustar 00000000000000use base64::{engine::general_purpose::STANDARD as B64, Engine}; use crypto_box::{self, aead::OsRng, PublicKey}; use octocrab::{ models::orgs::secrets::{CreateOrganizationSecret, Visibility}, Octocrab, }; use std::convert::TryInto; #[tokio::main] async fn main() -> octocrab::Result<()> { let token = std::env::var("GITHUB_TOKEN").expect("GITHUB_TOKEN env variable is required"); let octocrab = Octocrab::builder().personal_token(token).build()?; let org = octocrab.orgs("owner"); let secrets = org.secrets(); let public_key = secrets.get_public_key().await?; let crypto_pk = { let org_pk_bytes = B64.decode(public_key.key).unwrap(); let pk_array: [u8; crypto_box::KEY_SIZE] = org_pk_bytes.try_into().unwrap(); PublicKey::from(pk_array) }; let encrypted_value = crypto_box::seal(&mut OsRng, &crypto_pk, b"Very secret value").unwrap(); let result = secrets .create_or_update_secret( "TEST_SECRET_RS", &CreateOrganizationSecret { encrypted_value: &B64.encode(encrypted_value), key_id: &public_key.key_id, visibility: Visibility::Private, selected_repository_ids: None, }, ) .await?; println!("{:?}", result); Ok(()) } octocrab-0.31.2/examples/create_repo_from_template.rs000064400000000000000000000010741046102023000210770ustar 00000000000000use octocrab::Octocrab; #[tokio::main] async fn main() -> octocrab::Result<()> { let token = std::env::var("GITHUB_TOKEN").expect("GITHUB_TOKEN env variable is required"); let octocrab = Octocrab::builder() .personal_token(token.to_string()) .build()?; let repository = octocrab.repos("rust-lang", "rust-template"); repository .generate("new-repository") .owner("new-owner") .description("New description") .private(true) .include_all_branches(true) .send() .await?; Ok(()) } octocrab-0.31.2/examples/create_repo_secret.rs000064400000000000000000000022621046102023000175260ustar 00000000000000use base64::{engine::general_purpose::STANDARD as B64, Engine}; use crypto_box::{self, aead::OsRng, PublicKey}; use octocrab::{models::repos::secrets::CreateRepositorySecret, Octocrab}; use std::convert::TryInto; #[tokio::main] async fn main() -> octocrab::Result<()> { let token = std::env::var("GITHUB_TOKEN").expect("GITHUB_TOKEN env variable is required"); let octocrab = Octocrab::builder().personal_token(token).build()?; let repo = octocrab.repos("owner", "repo"); let secrets = repo.secrets(); let public_key = secrets.get_public_key().await?; let crypto_pk = { let pk_bytes = B64.decode(public_key.key).unwrap(); let pk_array: [u8; crypto_box::KEY_SIZE] = pk_bytes.try_into().unwrap(); PublicKey::from(pk_array) }; let encrypted_value = crypto_box::seal(&mut OsRng, &crypto_pk, b"Very secret value").unwrap(); let result = secrets .create_or_update_secret( "TEST_SECRET_RS", &CreateRepositorySecret { encrypted_value: &B64.encode(encrypted_value), key_id: &public_key.key_id, }, ) .await?; println!("{:?}", result); Ok(()) } octocrab-0.31.2/examples/custom_client.rs000064400000000000000000000025631046102023000165450ustar 00000000000000use http::header::USER_AGENT; use http::Uri; use hyper_rustls::HttpsConnectorBuilder; use octocrab::service::middleware::base_uri::BaseUriLayer; use octocrab::service::middleware::extra_headers::ExtraHeadersLayer; use octocrab::{AuthState, OctocrabBuilder}; use std::sync::Arc; #[tokio::main] async fn main() -> octocrab::Result<()> { let connector = HttpsConnectorBuilder::new() .with_native_roots() // enabled the `rustls-native-certs` feature in hyper-rustls .https_only() .enable_http1() .build(); let client = hyper::Client::builder().build(connector); let octocrab = OctocrabBuilder::new_empty() .with_service(client) .with_layer(&BaseUriLayer::new(Uri::from_static( "https://api.github.com", ))) .with_layer(&ExtraHeadersLayer::new(Arc::new(vec![( USER_AGENT, "octocrab".parse().unwrap(), )]))) .with_auth(AuthState::None) .build() .unwrap(); let repo = octocrab.repos("rust-lang", "rust").get().await?; let repo_metrics = octocrab .repos("rust-lang", "rust") .get_community_profile_metrics() .await?; println!( "{} has {} stars and {}% health percentage", repo.full_name.unwrap(), repo.stargazers_count.unwrap_or(0), repo_metrics.health_percentage ); Ok(()) } octocrab-0.31.2/examples/delete_repo.rs000064400000000000000000000005001046102023000161510ustar 00000000000000use octocrab::Octocrab; #[tokio::main] async fn main() -> octocrab::Result<()> { let token = std::env::var("GITHUB_TOKEN").expect("GITHUB_TOKEN env variable is required"); let octocrab = Octocrab::builder().personal_token(token).build()?; octocrab.repos("rust-lang", "rust").delete().await?; Ok(()) } octocrab-0.31.2/examples/device_flow.rs000064400000000000000000000032351046102023000161600ustar 00000000000000use either::Either; use http::header::ACCEPT; use std::time::Duration; #[tokio::main] async fn main() -> octocrab::Result<()> { let client_id = secrecy::Secret::from(std::env::var("GITHUB_CLIENT_ID").unwrap()); let crab = octocrab::Octocrab::builder() .base_uri("https://github.com")? .add_header(ACCEPT, "application/json".to_string()) .build()?; let codes = crab .authenticate_as_device(&client_id, ["public_repo", "read:org"]) .await?; println!( "Go to {} and enter code {}", codes.verification_uri, codes.user_code ); let mut interval = Duration::from_secs(codes.interval); let mut clock = tokio::time::interval(interval); let auth = loop { clock.tick().await; match codes.poll_once(&crab, &client_id).await? { Either::Left(auth) => break auth, Either::Right(cont) => match cont { octocrab::auth::Continue::SlowDown => { // We were request to slow down. We add five seconds to the polling // duration. interval += Duration::from_secs(5); clock = tokio::time::interval(interval); // The first tick happens instantly, so we tick that off immediately. clock.tick().await; } octocrab::auth::Continue::AuthorizationPending => { // The user has not clicked authorize yet, but nothing has gone wrong. // We keep polling. } }, } }; println!("Authorization succeeded with access to {:?}", auth.scope); Ok(()) } octocrab-0.31.2/examples/get_commit.rs000064400000000000000000000012001046102023000160070ustar 00000000000000use octocrab::Octocrab; #[tokio::main] async fn main() -> octocrab::Result<()> { let token = std::env::var("GITHUB_TOKEN").expect("GITHUB_TOKEN env variable is required"); let octocrab = Octocrab::builder().personal_token(token).build()?; let commit = octocrab .commits("XAMPPRocky", "octocrab") .get("15c0e31") .await?; for file in commit.files.unwrap() { println!( "File: {file}, Additions: {additions}, Deletions: {deletions}", file = file.filename, additions = file.additions, deletions = file.deletions, ); } Ok(()) } octocrab-0.31.2/examples/get_content.rs000064400000000000000000000007051046102023000162020ustar 00000000000000use octocrab::Octocrab; #[tokio::main] async fn main() -> octocrab::Result<()> { let token = std::env::var("GITHUB_TOKEN").expect("GITHUB_TOKEN env variable is required"); let octocrab = Octocrab::builder().personal_token(token).build()?; let content = octocrab .repos("rust-lang", "rust") .get_content() .send() .await?; println!("{} files/dirs in the repo root", content.items.len()); Ok(()) } octocrab-0.31.2/examples/get_rate_limit.rs000064400000000000000000000010201046102023000166500ustar 00000000000000use octocrab::Octocrab; #[tokio::main] async fn main() -> Result<(), Box> { let ratelimit = octocrab::instance().ratelimit().get().await?; println!("{}", serde_json::to_string_pretty(&ratelimit)?); let token = std::env::var("GITHUB_TOKEN").expect("GITHUB_TOKEN env variable is required"); let octocrab = Octocrab::builder().personal_token(token).build()?; let ratelimit = octocrab.ratelimit().get().await?; println!("{}", serde_json::to_string_pretty(&ratelimit)?); Ok(()) } octocrab-0.31.2/examples/get_repo.rs000064400000000000000000000012071046102023000154730ustar 00000000000000use octocrab::Octocrab; #[tokio::main] async fn main() -> octocrab::Result<()> { let token = std::env::var("GITHUB_TOKEN").expect("GITHUB_TOKEN env variable is required"); let octocrab = Octocrab::builder().personal_token(token).build()?; let repo = octocrab.repos("rust-lang", "rust").get().await?; let repo_metrics = octocrab .repos("rust-lang", "rust") .get_community_profile_metrics() .await?; println!( "{} has {} stars and {}% health percentage", repo.full_name.unwrap(), repo.stargazers_count.unwrap_or(0), repo_metrics.health_percentage ); Ok(()) } octocrab-0.31.2/examples/github_app_authentication.rs000064400000000000000000000012531046102023000211110ustar 00000000000000use octocrab::models::InstallationRepositories; use octocrab::Octocrab; #[tokio::main] async fn main() -> octocrab::Result<()> { let app_id = read_env_var("GITHUB_APP_ID").parse::().unwrap().into(); let app_private_key = read_env_var("GITHUB_APP_PRIVATE_KEY"); let key = jsonwebtoken::EncodingKey::from_rsa_pem(app_private_key.as_bytes()).unwrap(); let octocrab = Octocrab::builder().app(app_id, key).build()?; let _installations = octocrab.apps().installations().send().await.unwrap(); Ok(()) } fn read_env_var(var_name: &str) -> String { let err = format!("Missing environment variable: {var_name}"); std::env::var(var_name).expect(&err) } octocrab-0.31.2/examples/github_app_authentication_manual.rs000064400000000000000000000030621046102023000224460ustar 00000000000000use octocrab::models::{InstallationRepositories, InstallationToken}; use octocrab::params::apps::CreateInstallationAccessToken; use octocrab::Octocrab; #[tokio::main] async fn main() -> octocrab::Result<()> { let app_id = read_env_var("GITHUB_APP_ID"); let app_private_key = read_env_var("GITHUB_APP_PRIVATE_KEY"); let key = jsonwebtoken::EncodingKey::from_rsa_pem(app_private_key.as_bytes()).unwrap(); let token = octocrab::auth::create_jwt(app_id.parse::().unwrap().into(), &key).unwrap(); let octocrab = Octocrab::builder().personal_token(token).build()?; let installations = octocrab .apps() .installations() .send() .await .unwrap() .take_items(); let mut create_access_token = CreateInstallationAccessToken::default(); create_access_token.repositories = vec!["octocrab".to_string()]; let access: InstallationToken = octocrab .post( installations[0].access_tokens_url.as_ref().unwrap(), Some(&create_access_token), ) .await .unwrap(); let octocrab = octocrab::OctocrabBuilder::new() .personal_token(access.token) .build() .unwrap(); let installed_repos: InstallationRepositories = octocrab .get("/installation/repositories", None::<&()>) .await .unwrap(); let _repos = installed_repos.repositories; Ok(()) } fn read_env_var(var_name: &str) -> String { let err = format!("Missing environment variable: {var_name}"); std::env::var(var_name).expect(&err) } octocrab-0.31.2/examples/is_collab.rs000064400000000000000000000010471046102023000156200ustar 00000000000000use octocrab::Octocrab; #[tokio::main] async fn main() -> octocrab::Result<()> { let token = std::env::var("GITHUB_TOKEN").expect("GITHUB_TOKEN env variable is required"); let octocrab = Octocrab::builder().personal_token(token).build()?; let repo = octocrab .repos("rust-lang", "rust") .is_collaborator("Roger-luo") .await?; if repo { println!("Roger-luo is a collaborator of rust-lang/rust"); } else { println!("Roger-luo is not a collaborator of rust-lang/rust"); } Ok(()) } octocrab-0.31.2/examples/list_all_workflow_runs.rs000064400000000000000000000014741046102023000205010ustar 00000000000000use octocrab::Octocrab; #[tokio::main] async fn main() -> octocrab::Result<()> { let octocrab = Octocrab::builder().build()?; let runs = octocrab .workflows("rust-lang-ci", "rust") .list_all_runs() .per_page(2) .branch("master") .event("push") .status("success") .send() .await?; for run in runs { println!("Run:"); println!(" ID: {}", run.id); println!(" Name: {}", run.name); println!(" Event: {}", run.event); println!(" Branch: {}", run.head_branch); println!(" Created At: {}", run.created_at); println!(" Commit:"); println!(" Author: {}", run.head_commit.author.name); println!(" Message: {}", run.head_commit.message); println!() } Ok(()) } octocrab-0.31.2/examples/list_forks.rs000064400000000000000000000010621046102023000160450ustar 00000000000000use octocrab::{params::repos::forks::Sort, Octocrab}; #[tokio::main] async fn main() -> octocrab::Result<()> { let token = std::env::var("GITHUB_TOKEN").expect("GITHUB_TOKEN env variable is required"); let octocrab = Octocrab::builder().personal_token(token).build()?; let forks = octocrab .repos("rust-lang", "rust") .list_forks() .sort(Sort::Oldest) .page(2u32) .per_page(35) .send() .await?; for f in forks { println!("fork: {}", f.owner.unwrap().login); } Ok(()) } octocrab-0.31.2/examples/list_gists_for_token_holder.rs000064400000000000000000000024351046102023000214620ustar 00000000000000use octocrab::Octocrab; #[tokio::main] async fn main() -> octocrab::Result<()> { let token = std::env::var("GITHUB_TOKEN").expect("GITHUB_TOKEN env variable is required"); let octocrab = Octocrab::builder().personal_token(token).build()?; let current_user_name = octocrab.current().user().await?.login; let mut current_gist_page = octocrab .current() .list_gists_for_authenticated_user() .per_page(1) .send() .await?; let mut gists = current_gist_page.take_items(); while let Ok(Some(mut new_page)) = octocrab.get_page(¤t_gist_page.next).await { gists.extend(new_page.take_items()); current_gist_page = new_page; } println!( "User '{username}' has {count} gists:", username = current_user_name, count = gists.len() ); println!("id | url | [files...] | description"); for gist in gists { println!( "{id} | {url} | [{files}] | {description}", id = gist.id, url = gist.html_url, files = gist.files.into_keys().collect::>().join(", "), description = gist .description .unwrap_or("".into()) .escape_default(), ); } Ok(()) } octocrab-0.31.2/examples/list_repos_for_authenticated_user.rs000064400000000000000000000010221046102023000226530ustar 00000000000000use octocrab::Octocrab; #[tokio::main] async fn main() -> octocrab::Result<()> { let token = std::env::var("GITHUB_TOKEN").expect("GITHUB_TOKEN env variable is required"); let octocrab = Octocrab::builder().personal_token(token).build()?; let my_repos = octocrab .current() .list_repos_for_authenticated_user() .type_("owner") .sort("updated") .per_page(100) .send() .await?; for repo in my_repos { println!("{}", repo.name); } Ok(()) } octocrab-0.31.2/examples/list_repos_starred_by_authenticated_user.rs000064400000000000000000000007731046102023000242370ustar 00000000000000use octocrab::Octocrab; #[tokio::main] async fn main() -> octocrab::Result<()> { let token = std::env::var("GITHUB_TOKEN").expect("GITHUB_TOKEN env variable is required"); let octocrab = Octocrab::builder().personal_token(token).build()?; let repos = octocrab .current() .list_repos_starred_by_authenticated_user() .sort("updated") .per_page(100) .send() .await?; for repo in repos { println!("{}", repo.name); } Ok(()) } octocrab-0.31.2/examples/merge_branch.rs000064400000000000000000000007051046102023000163050ustar 00000000000000use octocrab::Octocrab; #[tokio::main] async fn main() -> octocrab::Result<()> { let token = std::env::var("GITHUB_TOKEN").expect("GITHUB_TOKEN env variable is required"); let octocrab = Octocrab::builder().personal_token(token).build()?; octocrab .repos("XAMPPRocky", "octocrab") .merge("feature/1", "master") .commit_message("This is a custom merge-commit message") .send() .await?; Ok(()) } octocrab-0.31.2/examples/notifications.rs000064400000000000000000000007731046102023000165470ustar 00000000000000use octocrab::Octocrab; #[tokio::main] async fn main() -> octocrab::Result<()> { let token = std::env::var("GITHUB_TOKEN").expect("GITHUB_TOKEN env variable is required"); let octocrab = Octocrab::builder().personal_token(token).build()?; let notifications = octocrab .activity() .notifications() .list() .all(true) .send() .await?; for n in notifications { println!("unread notification: {}", n.subject.title); } Ok(()) } octocrab-0.31.2/examples/paging_results.rs000064400000000000000000000011641046102023000167170ustar 00000000000000use octocrab::{params, Octocrab}; #[tokio::main] async fn main() -> octocrab::Result<()> { let octocrab = Octocrab::default(); let mut current_page = octocrab .orgs("rust-lang") .list_repos() .repo_type(params::repos::Type::Sources) .per_page(100) .send() .await?; let mut prs = current_page.take_items(); while let Ok(Some(mut new_page)) = octocrab.get_page(¤t_page.next).await { prs.extend(new_page.take_items()); for pr in prs.drain(..) { println!("{pr:?}"); } current_page = new_page; } Ok(()) } octocrab-0.31.2/examples/poll_events.rs000064400000000000000000000021101046102023000162130ustar 00000000000000use octocrab::{etag::Etagged, models::events::Event, Page}; use std::collections::VecDeque; const DELAY_MS: u64 = 500; const TRACKING_CAPACITY: usize = 200; #[tokio::main] async fn main() -> octocrab::Result<()> { let mut etag = None; let mut seen = VecDeque::with_capacity(TRACKING_CAPACITY); let octo = octocrab::instance(); loop { let response: Etagged> = octo.events().etag(etag).per_page(100).send().await?; if let Some(page) = response.value { for event in page { if !seen.contains(&event.id) { println!( "New event : id = {:?}, type = {:?}, time = {:?}", event.id, event.r#type, event.created_at, ); if seen.len() == TRACKING_CAPACITY { seen.pop_back(); } seen.push_front(event.id); } } } etag = response.etag; tokio::time::sleep(tokio::time::Duration::from_millis(DELAY_MS)).await; } } octocrab-0.31.2/examples/poll_org_events.rs000064400000000000000000000030071046102023000170700ustar 00000000000000use octocrab::{etag::Etagged, models::events::Event, Page}; use std::collections::VecDeque; const DELAY_MS: u64 = 500; const TRACKING_CAPACITY: usize = 20; #[tokio::main] async fn main() -> octocrab::Result<()> { let mut etag = None; let mut seen = VecDeque::with_capacity(TRACKING_CAPACITY); let octo = octocrab::instance(); loop { let response: Etagged> = octo .orgs("nixos") .events() .etag(etag) .per_page(10) .send() .await?; if let Some(page) = response.value { for event in page { // If an etag changes and we get a new page, this page may contain events we have // already seen along with new events. So, keep track of the ones we have seen for // each page, this will be at most 20 events - the current page of 10 events and // the last page. if !seen.contains(&event.id) { println!( "New event : id = {:?}, repo = {:?}, type = {:?}, time = {:?}", event.id, event.repo.name, event.r#type, event.created_at ); if seen.len() == TRACKING_CAPACITY { seen.pop_back(); } seen.push_front(event.id); } } } etag = response.etag; tokio::time::sleep(tokio::time::Duration::from_millis(DELAY_MS)).await; } } octocrab-0.31.2/examples/poll_repo_events.rs000064400000000000000000000027651046102023000172600ustar 00000000000000use octocrab::{etag::Etagged, models::events::Event, Page}; use std::collections::VecDeque; const DELAY_MS: u64 = 500; const TRACKING_CAPACITY: usize = 20; #[tokio::main] async fn main() -> octocrab::Result<()> { let mut etag = None; let mut seen = VecDeque::with_capacity(TRACKING_CAPACITY); let octo = octocrab::instance(); loop { let response: Etagged> = octo .repos("nixos", "nixpkgs") .events() .etag(etag) .per_page(10) .send() .await?; if let Some(page) = response.value { for event in page { // If an etag changes and we get a new page, this page may contain events we have // already seen along with new events. So, keep track of the ones we have seen for // each page, this will be at most 20 events - the current page of 10 events and // the last page. if !seen.contains(&event.id) { println!( "New event : id = {:?}, type = {:?}, time = {:?}", event.id, event.r#type, event.created_at ); if seen.len() == TRACKING_CAPACITY { seen.pop_back(); } seen.push_front(event.id); } } } etag = response.etag; tokio::time::sleep(tokio::time::Duration::from_millis(DELAY_MS)).await; } } octocrab-0.31.2/examples/print_license.rs000064400000000000000000000003251046102023000165250ustar 00000000000000#[tokio::main] async fn main() -> octocrab::Result<()> { let license = octocrab::instance() .repos("rust-lang", "rust") .license() .await?; println!("{license:#?}"); Ok(()) } octocrab-0.31.2/examples/print_pr_diff.rs000064400000000000000000000003221046102023000165110ustar 00000000000000#[tokio::main] async fn main() -> octocrab::Result<()> { let diff = octocrab::instance() .pulls("rust-lang", "rust") .get_diff(72033) .await?; println!("{diff}"); Ok(()) } octocrab-0.31.2/examples/render_markdown.rs000064400000000000000000000003161046102023000170500ustar 00000000000000#[tokio::main] async fn main() -> octocrab::Result<()> { let markdown = "**Markdown**"; print!( "{}", octocrab::instance().markdown().render_raw(markdown).await? ); Ok(()) } octocrab-0.31.2/examples/search_issues.rs000064400000000000000000000007061046102023000165320ustar 00000000000000#[tokio::main] async fn main() -> Result<(), Box> { let octocrab = octocrab::Octocrab::builder() .personal_token(std::env::var("GITHUB_TOKEN").unwrap()) .build()?; match octocrab .search() .issues_and_pull_requests("tokei is:pr") .send() .await { Ok(page) => println!("{page:#?}"), Err(error) => println!("{error:#?}"), } Ok(()) } octocrab-0.31.2/examples/star_unstar_a_gist.rs000064400000000000000000000045341046102023000175700ustar 00000000000000use octocrab::Octocrab; struct ProgramArguments { gist_id: String, star: bool, } /// Rudimentary CLI interface. Tries to be nice to the caller without too /// much bloat on the example. fn parse_argv_or_exit() -> ProgramArguments { const USAGE: &str = r#"Usage: (--star | --unstar) GIST_ID"#; let star = if let Some(param) = std::env::args().nth(1) { if param == "--star" { true } else if param == "--unstar" { false } else { eprintln!("error: Need (--star | --unstar) as first argument."); eprintln!("{}", USAGE); std::process::exit(1); } } else { eprintln!("error: Need (--star | --unstar) as first argument."); eprintln!("{}", USAGE); std::process::exit(1); }; let gist_id = if let Some(gist_id) = std::env::args().nth(2) { gist_id } else { eprintln!("error: Need GIST_ID as second argument."); eprintln!("{}", USAGE); std::process::exit(1); }; ProgramArguments { gist_id, star } } /// This example tries to demonstrate interacting with a gists' 'stars'. /// It does so by making a program that takes two CLI arguments: /// /// 1) `--star` or `--unstar` to either star/unstar a gist /// 2) A `GIST_ID` to identify which gist to operate on /// /// The example will check if a gist is already starred / unstarred, before /// performing the operation. #[tokio::main] async fn main() -> octocrab::Result<()> { let token = std::env::var("GITHUB_TOKEN").expect("GITHUB_TOKEN env variable is required"); let args = parse_argv_or_exit(); let octocrab = Octocrab::builder().personal_token(token).build()?; let gists_handler = octocrab.gists(); let is_starred = gists_handler.is_starred(&args.gist_id).await?; if is_starred && args.star { println!("{gist_id} is already starred.", gist_id = &args.gist_id); return Ok(()); } if !is_starred && !args.star { println!("{gist_id} is already un-starred.", gist_id = &args.gist_id); return Ok(()); } if args.star { gists_handler.star(&args.gist_id).await?; println!("Starred {gist_id}.", gist_id = &args.gist_id) } else { gists_handler.unstar(&args.gist_id).await?; println!("Un-starred {gist_id}.", gist_id = &args.gist_id) } Ok(()) } octocrab-0.31.2/examples/unlock.rs000064400000000000000000000004601046102023000151620ustar 00000000000000#[tokio::main] async fn main() -> octocrab::Result<()> { let api = octocrab::instance(); let issues_api = api.issues("rust-lang", "rust"); let one_issue = issues_api.list().per_page(1).send().await?.take_items(); issues_api.unlock(one_issue.first().unwrap().number).await?; Ok(()) } octocrab-0.31.2/examples/update_pull_request_branch.rs000075500000000000000000000013211046102023000212720ustar 00000000000000#!/usr/bin/env rust-script //! Dependencies can be specified in the script file itself as follows: //! //! ```cargo //! [dependencies] //! tokio = { version = "1.6.1", default-features = false, features = ["macros", "rt-multi-thread", "time"] } //! octocrab = { path = "../" } //! ``` use octocrab::Octocrab; #[tokio::main] async fn main() -> octocrab::Result<()> { let token = std::env::var("GITHUB_TOKEN").expect("GITHUB_TOKEN env variable is required"); let octocrab = Octocrab::builder().personal_token(token).build()?; let update = octocrab .pulls("XAMPPRocky", "octocrab") .update_branch(200) .await?; println!("Result of pull request update: {update}",); Ok(()) } octocrab-0.31.2/release-plz.toml000064400000000000000000000000441046102023000146210ustar 00000000000000[workspace] pr_labels = ["release"] octocrab-0.31.2/src/api/actions.rs000064400000000000000000000376401046102023000150630ustar 00000000000000//! GitHub Actions use snafu::ResultExt; use crate::error::{HttpSnafu, HyperSnafu}; use crate::etag::{EntityTag, Etagged}; use crate::models::{ workflows::WorkflowDispatch, workflows::WorkflowListArtifact, ArtifactId, RepositoryId, RunId, }; use crate::{params, FromResponse, Octocrab, Page}; use http::request::Builder; use http::{header::HeaderMap, Method, StatusCode, Uri}; use hyper::body; pub struct ListWorkflowRunArtifacts<'octo> { crab: &'octo Octocrab, owner: String, repo: String, run_id: RunId, per_page: Option, page: Option, etag: Option, } impl<'octo> ListWorkflowRunArtifacts<'octo> { pub(crate) fn new(crab: &'octo Octocrab, owner: String, repo: String, run_id: RunId) -> Self { Self { crab, owner, repo, run_id, per_page: None, page: None, etag: None, } } /// Etag for this request. pub fn etag(mut self, etag: Option) -> Self { self.etag = etag; self } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } pub async fn send(self) -> crate::Result>> { let path = format!( "/repos/{owner}/{repo}/actions/runs/{run_id}/artifacts", owner = self.owner, repo = self.repo, run_id = self.run_id ); let uri = Uri::builder() .path_and_query(path) .build() .context(HttpSnafu)?; let mut headers = HeaderMap::new(); if let Some(etag) = self.etag { EntityTag::insert_if_none_match_header(&mut headers, etag)?; } let request = self .crab .build_request(Builder::new().method(Method::GET).uri(uri), None::<&()>)?; let response = self.crab.execute(request).await?; let etag = EntityTag::extract_from_response(&response); if response.status() == StatusCode::NOT_MODIFIED { Ok(Etagged { etag, value: None }) } else { >::from_response(crate::map_github_error(response).await?) .await .map(|page| Etagged { etag, value: Some(page), }) } } } pub struct WorkflowDispatchBuilder<'octo> { crab: &'octo Octocrab, owner: String, repo: String, workflow_id: String, data: WorkflowDispatch, } impl<'octo> WorkflowDispatchBuilder<'octo> { pub(crate) fn new( crab: &'octo Octocrab, owner: String, repo: String, workflow_id: String, r#ref: String, ) -> Self { let mut this = Self { crab, owner, repo, workflow_id, data: Default::default(), }; this.data.r#ref = r#ref; this } /// Input keys and values configured in the workflow file. The maximum number of properties is 10. /// Any default properties configured in the workflow file will be used when inputs are omitted. /// /// # Panics /// If `inputs` is not `Value::Object`. pub fn inputs(mut self, inputs: serde_json::Value) -> Self { assert!(inputs.is_object(), "Inputs should be a JSON object"); self.data.inputs = inputs; self } pub async fn send(self) -> crate::Result<()> { let route = format!( "/repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches", owner = self.owner, repo = self.repo, workflow_id = self.workflow_id ); let uri = Uri::builder() .path_and_query(route) .build() .context(HttpSnafu)?; let response = self.crab._post(uri, Some(&self.data)).await?; if !response.status().is_success() { return Err(crate::map_github_error(response).await.unwrap_err()); } Ok(()) } } /// Handler for GitHub's actions API. /// /// Created with [`Octocrab::actions`]. pub struct ActionsHandler<'octo> { crab: &'octo Octocrab, } impl<'octo> ActionsHandler<'octo> { pub(crate) fn new(crab: &'octo Octocrab) -> Self { Self { crab } } /// Adds a repository to an organization secret when the visibility for /// repository access is set to selected. The visibility is set when you /// create or update an organization secret. You must authenticate using an /// access token with the admin:org scope to use this endpoint. GitHub Apps /// must have the secrets organization permission to use this endpoint. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// octocrab::instance() /// .actions() /// .add_selected_repo_to_org_secret("org", "secret_name", 1234u64.into()) /// .await?; /// # Ok(()) /// # } /// ``` pub async fn add_selected_repo_to_org_secret( &self, org: impl AsRef, secret_name: impl AsRef, repository_id: RepositoryId, ) -> crate::Result<()> { let route = format!( "/orgs/{org}/actions/secrets/{secret_name}/repositories/{repository_id}", org = org.as_ref(), secret_name = secret_name.as_ref(), repository_id = repository_id, ); let uri = Uri::builder() .path_and_query(route) .build() .context(HttpSnafu)?; crate::map_github_error(self.crab._put(uri, None::<&()>).await?) .await .map(drop) } /// Removes a repository from an organization secret when the visibility for /// repository access is set to selected. The visibility is set when you /// create or update an organization secret. You must authenticate using an /// access token with the admin:org scope to use this endpoint. GitHub Apps /// must have the secrets organization permission to use this endpoint. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// octocrab::instance() /// .actions() /// .remove_selected_repo_from_org_secret("org", "secret_name", 1234u64.into()) /// .await?; /// # Ok(()) /// # } /// ``` pub async fn remove_selected_repo_from_org_secret( &self, org: impl AsRef, secret_name: impl AsRef, repository_id: RepositoryId, ) -> crate::Result<()> { let route = format!( "/orgs/{org}/actions/secrets/{secret_name}/repositories/{repository_id}", org = org.as_ref(), secret_name = secret_name.as_ref(), repository_id = repository_id, ); let uri = Uri::builder() .path_and_query(route) .build() .context(HttpSnafu)?; crate::map_github_error(self.crab._delete(uri, None::<&()>).await?) .await .map(drop) } /// Cancels a workflow run using its id. You must authenticate using an /// access token with the `repo` scope to use this endpoint. GitHub Apps /// must have the `actions:write` permission to use this endpoint. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// octocrab::instance() /// .actions() /// .cancel_workflow_run("owner", "repo", 1234u64.into()) /// .await?; /// # Ok(()) /// # } /// ``` pub async fn cancel_workflow_run( &self, owner: impl AsRef, repo: impl AsRef, run_id: RunId, ) -> crate::Result<()> { let route = format!( "/repos/{owner}/{repo}/actions/runs/{run_id}/cancel", owner = owner.as_ref(), repo = repo.as_ref(), run_id = run_id, ); let uri = Uri::builder() .path_and_query(route) .build() .context(HttpSnafu)?; crate::map_github_error(self.crab._post(uri, None::<&()>).await?) .await .map(drop) } async fn follow_location_to_data( &self, response: http::Response, ) -> crate::Result { let data_response = self.crab.follow_location_to_data(response).await?; let body = data_response.into_body(); body::to_bytes(body).await.context(HyperSnafu) } /// Downloads and returns the raw data representing a zip of the logs from /// the workflow run specified by `run_id`. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// octocrab::instance() /// .actions() /// .download_workflow_run_logs("owner", "repo", 1234u64.into()) /// .await?; /// # Ok(()) /// # } /// ``` pub async fn download_workflow_run_logs( &self, owner: impl AsRef, repo: impl AsRef, run_id: RunId, ) -> crate::Result { let route = format!( "/repos/{owner}/{repo}/actions/runs/{run_id}/logs", owner = owner.as_ref(), repo = repo.as_ref(), run_id = run_id, ); let uri = Uri::builder() .path_and_query(route) .build() .context(HttpSnafu)?; self.follow_location_to_data(self.crab._get(uri).await?) .await } /// Downloads and returns the raw data representing an artifact from a /// repository. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// use octocrab::params::actions::ArchiveFormat; /// /// octocrab::instance() /// .actions() /// .download_artifact("owner", "repo", 1234u64.into(), ArchiveFormat::Zip) /// .await?; /// # Ok(()) /// # } /// ``` pub async fn download_artifact( &self, owner: impl AsRef, repo: impl AsRef, artifact_id: ArtifactId, archive_format: params::actions::ArchiveFormat, ) -> crate::Result { let route = format!( "/repos/{owner}/{repo}/actions/artifacts/{artifact_id}/{archive_format}", owner = owner.as_ref(), repo = repo.as_ref(), artifact_id = artifact_id, archive_format = archive_format, ); let uri = Uri::builder() .path_and_query(route) .build() .context(HttpSnafu)?; self.follow_location_to_data(self.crab._get(uri).await?) .await } /// Deletes all logs for a workflow run. You must authenticate using an /// access token with the `repo` scope to use this endpoint. GitHub Apps /// must have the `actions:write` permission to use this endpoint. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// octocrab::instance() /// .actions() /// .delete_workflow_run_logs("owner", "repo", 1234u64.into()) /// .await?; /// # Ok(()) /// # } /// ``` pub async fn delete_workflow_run_logs( &self, owner: impl AsRef, repo: impl AsRef, run_id: RunId, ) -> crate::Result<()> { let route = format!( "/repos/{owner}/{repo}/actions/runs/{run_id}/logs", owner = owner.as_ref(), repo = repo.as_ref(), run_id = run_id, ); let uri = Uri::builder() .path_and_query(route) .build() .context(HttpSnafu)?; crate::map_github_error(self.crab._delete(uri, None::<&()>).await?) .await .map(drop) } /// Get an organization's public key, which you need to encrypt secrets. /// You need to encrypt a secret before you can create or update secrets. /// You must authenticate using an access token with the `admin:org` scope /// to use this endpoint. GitHub Apps must have the secrets organization /// permission to use this endpoint. /// /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); /// let org = octocrab.actions().get_org_public_key("org").await?; /// # Ok(()) /// # } /// ``` pub async fn get_org_public_key( &self, org: impl AsRef, ) -> crate::Result { let route = format!("/orgs/{org}/actions/secrets/public-key", org = org.as_ref()); self.crab.get(route, None::<&()>).await } /// Lists artifacts for a workflow run. Anyone with read access to the /// repository can use this endpoint. If the repository is private you /// must use an access token with the `repo` scope. GitHub Apps must have /// the `actions:read` permission to use this endpoint. pub fn list_workflow_run_artifacts( &self, owner: impl Into, repo: impl Into, run_id: RunId, ) -> ListWorkflowRunArtifacts<'_> { ListWorkflowRunArtifacts::new(self.crab, owner.into(), repo.into(), run_id) } /// Dispatch a workflow run. You must authenticate using an /// access token with the `repo` scope to use this endpoint. GitHub Apps /// must have the `actions:write` permission to use this endpoint. /// /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); /// octocrab.actions() /// .create_workflow_dispatch("org", "repo", "workflow.yaml", "ref") /// // optional /// .inputs(serde_json::json!({"my-key": "my-value"})) /// .send() /// .await?; /// # return Ok(()); /// # } /// ``` pub fn create_workflow_dispatch( &self, owner: impl Into, repo: impl Into, workflow_id: impl Into, r#ref: impl Into, ) -> WorkflowDispatchBuilder<'_> { WorkflowDispatchBuilder::new( self.crab, owner.into(), repo.into(), workflow_id.into(), r#ref.into(), ) } } /* #[derive(serde::Serialize)] pub struct RenderMarkdownBuilder<'octo, 'r, 'text> { #[serde(skip)] handler: &'r ActionsHandler<'octo>, text: &'text str, mode: Option, context: Option, } impl<'octo, 'r, 'text> RenderMarkdownBuilder<'octo, 'r, 'text> { pub(crate) fn new(handler: &'r ActionsHandler<'octo>, text: &'text str) -> Self { Self { handler, text, mode: None, context: None, } } /// The repository context to use when creating references in `Mode::Gfm`. /// Omit this parameter when using markdown mode. pub fn context>(mut self, context: impl Into>) -> Self { self.context = context.into().map(A::into); self } /// The rendering mode. pub fn mode(mut self, mode: impl Into>) -> Self { self.mode = mode.into(); self } /// Send the actual request. pub async fn send(self) -> crate::Result { self.handler .crab ._post(self.handler.crab.absolute_url("markdown")?, Some(&self)) .await? .text() .await .context(crate::error::Http) } } #[cfg(test)] mod tests { #[test] fn serialize() { let octocrab = crate::instance(); let handler = octocrab.markdown(); let render = handler .render("**Markdown**") .mode(crate::params::markdown::Mode::Gfm) .context("owner/repo"); assert_eq!( serde_json::to_value(render).unwrap(), serde_json::json!({ "text": "**Markdown**", "mode": "gfm", "context": "owner/repo", }) ) } } */ octocrab-0.31.2/src/api/activity/notifications.rs000064400000000000000000000235461046102023000201300ustar 00000000000000//! Github Notifications API use crate::error::HttpSnafu; use crate::models::activity::Notification; use crate::models::activity::ThreadSubscription; use crate::models::{NotificationId, ThreadId}; use crate::Octocrab; use crate::Page; use http::Uri; use snafu::ResultExt; type DateTime = chrono::DateTime; /// Handler for GitHub's notifications API. /// /// Created with [`ActivityHandler::notifications`]. /// **Note:** All of these methods require authentication using /// your GitHub Access Token with the right privileges. /// /// [`ActivityHandler::notifications`]: ../struct.ActivityHandler.html#method.notifications pub struct NotificationsHandler<'octo> { crab: &'octo Octocrab, } impl<'octo> NotificationsHandler<'octo> { pub(crate) fn new(crab: &'octo Octocrab) -> Self { Self { crab } } /// Gets a notification by their id. /// /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let thread = octocrab::instance() /// .activity() /// .notifications() /// .get(123u64.into()) /// .await?; /// # Ok(()) /// # } /// ``` pub async fn get(&self, id: NotificationId) -> crate::Result { let route = format!("/notifications/threads/{id}"); self.crab.get(route, None::<&()>).await } /// Marks a single thread as read. /// /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// octocrab::instance() /// .activity() /// .notifications() /// .mark_as_read(123u64.into()) /// .await?; /// # Ok(()) /// # } /// ``` pub async fn mark_as_read(&self, id: NotificationId) -> crate::Result<()> { let route = format!("/notifications/threads/{id}"); let uri = Uri::builder() .path_and_query(route) .build() .context(HttpSnafu)?; let response = self.crab._patch(uri, None::<&()>).await?; crate::map_github_error(response).await.map(drop) } /// Marks all notifications in a repository as read. /// /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// octocrab::instance() /// .activity() /// .notifications() /// .mark_repo_as_read("XAMPPRocky", "octocrab", None) /// .await?; /// # Ok(()) /// # } /// ``` pub async fn mark_repo_as_read( &self, owner: impl AsRef, repo: impl AsRef, last_read_at: impl Into>, ) -> crate::Result<()> { #[derive(serde::Serialize)] struct Inner { last_read_at: DateTime, } let body = last_read_at .into() .map(|last_read_at| Inner { last_read_at }); let route = format!("/repos/{}/{}/notifications", owner.as_ref(), repo.as_ref()); let uri = Uri::builder() .path_and_query(route) .build() .context(HttpSnafu)?; let response = self.crab._put(uri, body.as_ref()).await?; crate::map_github_error(response).await.map(drop) } /// Marks all notifications as read. /// /// If you provide a `last_read_at` parameter, /// anything updated since this time will not be marked as read. /// /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// octocrab::instance() /// .activity() /// .notifications() /// .mark_all_as_read(None) /// .await?; /// # Ok(()) /// # } /// ``` pub async fn mark_all_as_read( &self, last_read_at: impl Into>, ) -> crate::Result<()> { #[derive(serde::Serialize)] struct Inner { last_read_at: DateTime, } let body = last_read_at .into() .map(|last_read_at| Inner { last_read_at }); let uri = Uri::builder() .path_and_query("/notifications") .build() .context(HttpSnafu)?; let response = self.crab._put(uri, body.as_ref()).await?; crate::map_github_error(response).await.map(drop) } /// This checks to see if the current user is subscribed to a thread. /// /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let subscription = octocrab::instance() /// .activity() /// .notifications() /// .get_thread_subscription(123u64.into()) /// .await?; /// # Ok(()) /// # } /// ``` pub async fn get_thread_subscription( &self, thread: ThreadId, ) -> crate::Result { let route = format!("/notifications/threads/{thread}/subscription"); self.crab.get(route, None::<&()>).await } /// Ignore or unignore a thread subscription, that is enabled by watching a repository. /// /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let subscription = octocrab::instance() /// .activity() /// .notifications() /// .set_thread_subscription(123u64.into(), true) /// .await?; /// # Ok(()) /// # } /// ``` pub async fn set_thread_subscription( &self, thread: ThreadId, ignored: bool, ) -> crate::Result { #[derive(serde::Serialize)] struct Inner { ignored: bool, } let route = format!("/notifications/threads/{thread}/subscription"); let body = Inner { ignored }; self.crab.get(route, Some(&body)).await } /// Mutes the whole thread conversation until you comment or get mentioned. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// octocrab::instance() /// .activity() /// .notifications() /// .delete_thread_subscription(123u64.into()) /// .await?; /// # Ok(()) /// # } /// ``` pub async fn delete_thread_subscription(&self, thread: ThreadId) -> crate::Result<()> { let route = format!("/notifications/threads/{thread}/subscription"); let uri = Uri::builder() .path_and_query(route) .build() .context(HttpSnafu)?; let response = self.crab._delete(uri, None::<&()>).await?; crate::map_github_error(response).await.map(drop) } /// List all notifications for the current user, that are in a given repository. /// /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let notifications = octocrab::instance() /// .activity() /// .notifications() /// .list_for_repo("XAMPPRocky", "octocrab") /// // Also show notifications that are marked as read. /// .all(true) /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn list_for_repo( &self, owner: impl AsRef, repo: impl AsRef, ) -> ListNotificationsBuilder<'octo> { let route = format!("/repos/{}/{}/notifications", owner.as_ref(), repo.as_ref()); ListNotificationsBuilder::new(self.crab, route) } /// List all notifications for the current user. /// /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let notifications = octocrab::instance() /// .activity() /// .notifications() /// .list() /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn list(&self) -> ListNotificationsBuilder<'octo> { ListNotificationsBuilder::new(self.crab, "/notifications".to_string()) } } /// A builder pattern struct for listing pull requests. /// /// Created by [`NotificationsHandler::list`]. /// /// [`NotificationsHandler::list`]: ./struct.NotificationsHandler.html#method.list #[derive(serde::Serialize)] pub struct ListNotificationsBuilder<'octo> { #[serde(skip)] url: String, #[serde(skip)] crab: &'octo Octocrab, #[serde(skip_serializing_if = "Option::is_none")] all: Option, #[serde(skip_serializing_if = "Option::is_none")] participating: Option, #[serde(skip_serializing_if = "Option::is_none")] since: Option>, #[serde(skip_serializing_if = "Option::is_none")] before: Option>, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo> ListNotificationsBuilder<'octo> { fn new(crab: &'octo Octocrab, url: String) -> Self { Self { url, crab, all: None, participating: None, since: None, before: None, per_page: None, page: None, } } /// If set, show notifications marked as read. pub fn all(mut self, v: bool) -> Self { self.all = Some(v); self } /// If set, only shows notifications in which the user is directly participating or mentioned. pub fn participating(mut self, v: bool) -> Self { self.participating = Some(v); self } /// Only show notifications updated after the given time. pub fn since(mut self, since: chrono::DateTime) -> Self { self.since = Some(since); self } /// Only show notifications updated before the given time. pub fn before(mut self, before: chrono::DateTime) -> Self { self.before = Some(before); self } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Sends the actual request. pub async fn send(self) -> crate::Result> { self.crab.get(&self.url, Some(&self)).await } } octocrab-0.31.2/src/api/activity.rs000064400000000000000000000010671046102023000152510ustar 00000000000000//! Github Activity API use crate::Octocrab; pub mod notifications; /// Handler for GitHub's activity API. /// /// Created with [`Octocrab::activity`]. pub struct ActivityHandler<'octo> { crab: &'octo Octocrab, } impl<'octo> ActivityHandler<'octo> { pub(crate) fn new(crab: &'octo Octocrab) -> Self { Self { crab } } /// Creates a `NotificationsHandler` for the current authenticated user. pub fn notifications(&self) -> notifications::NotificationsHandler<'octo> { notifications::NotificationsHandler::new(self.crab) } } octocrab-0.31.2/src/api/apps/installations.rs000064400000000000000000000043561046102023000172500ustar 00000000000000use super::*; use crate::Page; /// A builder pattern struct for listing installations. /// /// created by [`AppsRequestHandler::installations`] /// /// [`AppsRequestHandler::installations`]: ./struct.AppsRequestHandler.html#method.installations #[derive(serde::Serialize)] pub struct InstallationsRequestBuilder<'octo, 'b> { #[serde(skip)] handler: &'b AppsRequestHandler<'octo>, #[serde(skip_serializing_if = "Option::is_none")] since: Option>, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo, 'b> InstallationsRequestBuilder<'octo, 'b> { pub(crate) fn new(handler: &'b AppsRequestHandler<'octo>) -> Self { Self { handler, per_page: None, page: None, since: None, } } /// Only installations created at or after this time are returned. pub fn since(mut self, since: impl Into>) -> Self { self.since = Some(since.into()); self } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Sends the actual request. pub async fn send(self) -> crate::Result> { let route = "/app/installations"; self.handler.http_get(route, Some(&self)).await } } #[cfg(test)] mod tests { #[tokio::test] async fn serialize() { let octocrab = crate::Octocrab::default(); let handler = octocrab.apps(); let yesterday = chrono::Utc::now() - chrono::Duration::days(1); let list = handler .installations() .since(yesterday) .per_page(100) .page(1u8); assert_eq!( serde_json::to_value(list).unwrap(), serde_json::json!({ "since": yesterday, "per_page": 100, "page": 1 }) ) } } octocrab-0.31.2/src/api/apps.rs000064400000000000000000000063101046102023000143540ustar 00000000000000use crate::{models::InstallationId, Octocrab}; use http::request::Builder; use http::Method; mod installations; /// A client to [GitHub's apps API][apps-api]. /// /// Created with [`Octocrab::apps`]. /// /// [apps-api]: https://docs.github.com/en/rest/reference/apps pub struct AppsRequestHandler<'octo> { crab: &'octo Octocrab, } impl<'octo> AppsRequestHandler<'octo> { pub(crate) fn new(crab: &'octo Octocrab) -> Self { Self { crab } } /// Get an installation for the authenticated app /// /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); /// use octocrab::models::InstallationId; /// /// let installation = octocrab /// .apps() /// .installation(InstallationId(1)) /// .await?; /// # Ok(()) /// # } /// ``` pub async fn installation( &self, installation_id: InstallationId, ) -> crate::Result { let route = format!("/app/installations/{installation_id}",); self.crab.get(&route, None::<&()>).await } /// Creates a new `InstallationsBuilder` that can be configured to filter /// listing installations. /// /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); /// use octocrab::params; /// /// let page = octocrab /// .apps() /// .installations() /// // Optional Parameters /// .since(chrono::Utc::now() - chrono::Duration::days(1)) /// .per_page(100) /// .page(5u32) /// // Send the request /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn installations(&self) -> installations::InstallationsRequestBuilder { installations::InstallationsRequestBuilder::new(self) } pub(crate) async fn http_get( &self, route: A, parameters: Option<&P>, ) -> crate::Result where A: AsRef, P: serde::Serialize + ?Sized, R: crate::FromResponse, { let request = Builder::new() .method(Method::GET) .uri(self.crab.parameterized_uri(route, parameters)?); let request = self.crab.build_request(request, None::<&()>)?; R::from_response(crate::map_github_error(self.crab.execute(request).await?).await?).await } /// Get a repository installation for the authenticated app. pub async fn get_repository_installation( &self, owner: impl AsRef, repo: impl AsRef, ) -> crate::Result { let route = format!( "/repos/{owner}/{repo}/installation", owner = owner.as_ref(), repo = repo.as_ref(), ); self.crab.get(&route, None::<&()>).await } /// Get an organization installation for the authenticated app. pub async fn get_org_installation( &self, owner: impl AsRef, ) -> crate::Result { let route = format!("/orgs/{owner}/installation", owner = owner.as_ref(),); self.crab.get(&route, None::<&()>).await } } octocrab-0.31.2/src/api/checks.rs000064400000000000000000000255401046102023000146570ustar 00000000000000use crate::models::{CheckRunId, CheckSuiteId}; use crate::params::repos::Commitish; use crate::{models, Octocrab, Result}; use chrono::{DateTime, Utc}; /// Handler for GitHub's Checks API. /// /// Created with [`Octocrab::checks`]. pub struct ChecksHandler<'octo> { crab: &'octo Octocrab, owner: String, repo: String, } #[derive(serde::Serialize)] #[serde(rename_all = "snake_case")] pub enum CheckRunStatus { Queued, InProgress, Completed, } #[derive(serde::Serialize)] pub struct CreateCheckRunBuilder<'octo, 'r> { #[serde(skip)] handler: &'r ChecksHandler<'octo>, name: String, head_sha: String, #[serde(skip_serializing_if = "Option::is_none")] details_url: Option, #[serde(skip_serializing_if = "Option::is_none")] external_id: Option, #[serde(skip_serializing_if = "Option::is_none")] status: Option, } impl<'octo, 'r> CreateCheckRunBuilder<'octo, 'r> { pub(crate) fn new(handler: &'r ChecksHandler<'octo>, name: String, head_sha: String) -> Self { Self { handler, name, head_sha, details_url: None, external_id: None, status: None, } } /// The URL of the integrator's site that has the full details of the check. /// If the integrator does not provide this, then the homepage of the GitHub app is used. pub fn details_url(mut self, details_url: impl Into) -> Self { self.details_url = Some(details_url.into()); self } /// A reference for the run on the integrator's system. pub fn external_id(mut self, external_id: impl Into) -> Self { self.external_id = Some(external_id.into()); self } /// The current status. /// Can be one of `queued`, `in_progress`, or `completed`. pub fn status(mut self, status: CheckRunStatus) -> Self { self.status = Some(status); self } /// Sends the actual request. pub async fn send(self) -> Result { let route = format!( "/repos/{owner}/{repo}/check-runs", owner = self.handler.owner, repo = self.handler.repo ); self.handler.crab.post(route, Some(&self)).await } } #[derive(serde::Serialize)] pub struct UpdateCheckRunBuilder<'octo, 'r> { #[serde(skip)] handler: &'r ChecksHandler<'octo>, check_run_id: CheckRunId, #[serde(skip_serializing_if = "Option::is_none")] name: Option, #[serde(skip_serializing_if = "Option::is_none")] details_url: Option, #[serde(skip_serializing_if = "Option::is_none")] external_id: Option, #[serde(skip_serializing_if = "Option::is_none")] started_at: Option>, #[serde(skip_serializing_if = "Option::is_none")] status: Option, #[serde(skip_serializing_if = "Option::is_none")] conclusion: Option, #[serde(skip_serializing_if = "Option::is_none")] completed_at: Option>, #[serde(skip_serializing_if = "Option::is_none")] output: Option, } impl<'octo, 'r> UpdateCheckRunBuilder<'octo, 'r> { pub(crate) fn new(handler: &'r ChecksHandler<'octo>, check_run_id: CheckRunId) -> Self { Self { handler, check_run_id, name: None, details_url: None, external_id: None, started_at: None, status: None, conclusion: None, completed_at: None, output: None, } } /// The name of the check. For example, "code-coverage". pub fn name(mut self, name: impl Into) -> Self { self.name = Some(name.into()); self } /// The URL of the integrator's site that has the full details of the check. /// If the integrator does not provide this, then the homepage of the GitHub app is used. pub fn details_url(mut self, details_url: impl Into) -> Self { self.details_url = Some(details_url.into()); self } /// A reference for the run on the integrator's system. pub fn external_url(mut self, external_id: impl Into) -> Self { self.external_id = Some(external_id.into()); self } /// The time that the check run began. pub fn started_at(mut self, started_at: DateTime) -> Self { self.started_at = Some(started_at); self } /// The current status. /// Can be one of `queued`, `in_progress`, or `completed`. pub fn status(mut self, status: CheckRunStatus) -> Self { self.status = Some(status); self } /// The final conclusion of the check. /// Can be one of `success`, `failure`, `neutral`, `cancelled`, `timed_out`, /// `skipped`, `stale` or `action_required`. pub fn conclusion(mut self, conclusion: impl Into) -> Self { self.conclusion = Some(conclusion.into()); self } /// The time that the check run completed. pub fn completed_at(mut self, completed_at: DateTime) -> Self { self.completed_at = Some(completed_at); self } /// Check runs can accept a variety of data in the output object, /// including a title and summary and can optionally provide /// descriptive details about the run. pub fn output(mut self, output: serde_json::Value) -> Self { self.output = Some(output); self } /// Sends the actual request. pub async fn send(self) -> Result { let route = format!( "/repos/{owner}/{repo}/check-runs/{check_run_id}", owner = self.handler.owner, repo = self.handler.repo, check_run_id = self.check_run_id ); self.handler.crab.patch(route, Some(&self)).await } } #[derive(serde::Serialize)] pub struct ListCheckRunsInCheckSuiteBuilder<'octo, 'r> { #[serde(skip)] handler: &'r ChecksHandler<'octo>, check_suite_id: CheckSuiteId, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo, 'r> ListCheckRunsInCheckSuiteBuilder<'octo, 'r> { pub(crate) fn new(handler: &'r ChecksHandler<'octo>, check_suite_id: CheckSuiteId) -> Self { Self { handler, check_suite_id, per_page: None, page: None, } } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Send the actual request. pub async fn send(self) -> Result { let route = format!( "/repos/{owner}/{repo}/check-suites/{check_suite_id}/check-runs", owner = self.handler.owner, repo = self.handler.repo, check_suite_id = self.check_suite_id, ); self.handler.crab.get(route, Some(&self)).await } } #[derive(serde::Serialize)] pub struct ListCheckRunsForGitRefBuilder<'octo, 'r> { #[serde(skip)] handler: &'r ChecksHandler<'octo>, #[serde(skip)] git_ref: Commitish, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo, 'r> ListCheckRunsForGitRefBuilder<'octo, 'r> { pub(crate) fn new(handler: &'r ChecksHandler<'octo>, git_ref: Commitish) -> Self { Self { handler, git_ref, per_page: None, page: None, } } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Send the actual request. pub async fn send(self) -> Result { let route = format!( "/repos/{owner}/{repo}/commits/{ref}/check-runs", owner = self.handler.owner, repo = self.handler.repo, ref = self.git_ref, ); self.handler.crab.get(route, Some(&self)).await } } impl<'octo> ChecksHandler<'octo> { pub(crate) fn new(crab: &'octo Octocrab, owner: String, repo: String) -> Self { Self { crab, owner, repo } } /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let check_runs = octocrab::instance() /// .checks("owner", "repo") /// .list_check_runs_in_a_check_suite(123456.into()) /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn list_check_runs_in_a_check_suite( &self, suite_id: CheckSuiteId, ) -> ListCheckRunsInCheckSuiteBuilder<'_, '_> { ListCheckRunsInCheckSuiteBuilder::new(self, suite_id) } /// ```no_run /// # use octocrab::params::repos::Commitish; /// async fn run() -> octocrab::Result<()> { /// let check_runs = octocrab::instance() /// .checks("owner", "repo") /// .list_check_runs_for_git_ref(Commitish("ref".to_string())) /// .send() /// .await?; /// # Ok(()) /// # } pub fn list_check_runs_for_git_ref( &self, git_ref: Commitish, ) -> ListCheckRunsForGitRefBuilder<'_, '_> { ListCheckRunsForGitRefBuilder::new(self, git_ref) } /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let check_run = octocrab::instance() /// .checks("owner", "repo") /// .create_check_run("name", "head_sha") /// .details_url("https://example.com") /// .external_id("external_id") /// .status(octocrab::checks::CheckRunStatus::InProgress) /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn create_check_run( &self, name: impl Into, head_sha: impl Into, ) -> CreateCheckRunBuilder<'_, '_> { CreateCheckRunBuilder::new(self, name.into(), head_sha.into()) } /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let check_run = octocrab::instance() /// .checks("owner", "repo") /// .update_check_run(123456.into()) /// .name("name") /// .details_url("https://example.com") /// .external_url("external_id") /// .status(octocrab::checks::CheckRunStatus::InProgress) /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn update_check_run(&self, check_run_id: CheckRunId) -> UpdateCheckRunBuilder<'_, '_> { UpdateCheckRunBuilder::new(self, check_run_id) } } octocrab-0.31.2/src/api/commits/associated_check_runs.rs000064400000000000000000000027351046102023000214160ustar 00000000000000use crate::commits::CommitHandler; use crate::models::checks::ListCheckRuns; use crate::params::repos::Reference; use crate::Result; #[derive(serde::Serialize)] pub struct AssociatedCheckRunsBuilder<'octo, 'r> { #[serde(skip)] handler: &'r CommitHandler<'octo>, #[serde(skip)] reference: Reference, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo, 'r> AssociatedCheckRunsBuilder<'octo, 'r> { pub(crate) fn new(handler: &'r CommitHandler<'octo>, reference: impl Into) -> Self { Self { handler, reference: reference.into(), per_page: None, page: None, } } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Send the actual request. pub async fn send(self) -> Result { let route = format!( "/repos/{owner}/{repo}/commits/{reference}/check-runs", owner = self.handler.owner, repo = self.handler.repo, reference = self.reference.full_ref_url() ); self.handler.crab.get(route, Some(&self)).await } } octocrab-0.31.2/src/api/commits/associated_pull_requests.rs000064400000000000000000000047421046102023000222010ustar 00000000000000use super::*; /// helper to let users know they can pass a branch name or a commit sha #[derive(Clone, Debug, serde::Serialize)] #[serde(untagged)] pub enum PullRequestTarget { Branch(String), Sha(String), } impl ToString for PullRequestTarget { fn to_string(&self) -> String { match self { Self::Branch(branch) => branch.to_string(), Self::Sha(commit) => commit.to_string(), } } } #[derive(serde::Serialize)] pub struct AssociatedPullRequestsBuilder<'octo, 'r> { #[serde(skip)] handler: &'r super::CommitHandler<'octo>, target: PullRequestTarget, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo, 'r> AssociatedPullRequestsBuilder<'octo, 'r> { /// Sha will return all closed pull requests for the given commit sha. /// /// Pass a Branch to return all open pull requests against that branch. pub(crate) fn new(handler: &'r super::CommitHandler<'octo>, target: PullRequestTarget) -> Self { Self { handler, target, page: None, per_page: None, } } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Sends the actual request. pub async fn send(self) -> crate::Result> { let route = format!( "/repos/{owner}/{repo}/commits/{target}/pulls", owner = self.handler.owner, repo = self.handler.repo, target = self.target.to_string(), ); self.handler.crab.get(route, Some(&self)).await } } #[cfg(test)] mod tests { #[tokio::test] async fn associated_pull_requests_serializes_correctly() { use super::PullRequestTarget; let octocrab = crate::Octocrab::default(); let handler = octocrab.commits("owner", "repo"); let associated_prs = handler.associated_pull_requests(PullRequestTarget::Sha("commit_sha".to_string())); assert_eq!( serde_json::to_value(associated_prs).unwrap(), serde_json::json!({ "target": "commit_sha" }) ); } } octocrab-0.31.2/src/api/commits/compare_commit.rs000064400000000000000000000035511046102023000200660ustar 00000000000000use super::*; #[derive(serde::Serialize)] pub struct CompareCommitsBuilder<'octo, 'r> { #[serde(skip)] handler: &'r super::CommitHandler<'octo>, base: String, head: String, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo, 'r> CompareCommitsBuilder<'octo, 'r> { pub(crate) fn new( handler: &'r super::CommitHandler<'octo>, base: String, head: String, ) -> Self { Self { handler, base, head, page: None, per_page: None, } } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Sends the actual request. pub async fn send(self) -> crate::Result { let route = format!( "/repos/{owner}/{repo}/compare/{base}...{head}", owner = self.handler.owner, repo = self.handler.repo, base = self.base, head = self.head, ); self.handler.crab.get(route, Some(&self)).await } } #[cfg(test)] mod tests { #[tokio::test] async fn compare_commits_serializes_correctly() { let octocrab = crate::Octocrab::default(); let handler = octocrab.commits("owner", "repo"); let comparison = handler.compare("base", "head"); assert_eq!( serde_json::to_value(comparison).unwrap(), serde_json::json!({ "base": "base", "head": "head", }) ); } } octocrab-0.31.2/src/api/commits/create_comment.rs000064400000000000000000000046661046102023000200650ustar 00000000000000use super::*; #[derive(serde::Serialize)] pub struct CreateCommentBuilder<'octo, 'r> { #[serde(skip)] handler: &'r super::CommitHandler<'octo>, sha: String, body: String, #[serde(skip_serializing_if = "Option::is_none")] path: Option, #[serde(skip_serializing_if = "Option::is_none")] position: Option, #[serde(skip_serializing_if = "Option::is_none")] line: Option, } impl<'octo, 'r> CreateCommentBuilder<'octo, 'r> { pub(crate) fn new(handler: &'r super::CommitHandler<'octo>, sha: String, body: String) -> Self { Self { handler, sha, body, path: None, position: None, line: None, } } /// Sends the actual request. pub async fn send(self) -> crate::Result { let route = format!( "/repos/{owner}/{repo}/commits/{commit_sha}/comments", owner = self.handler.owner, repo = self.handler.repo, commit_sha = self.sha, ); self.handler.crab.post(route, Some(&self)).await } /// Relative path of the file to comment on. /// /// Required if you provide position. /// /// For example, if you want to comment on a line in the file /// `lib/octocat.rb`, you would provide `lib/octocat.rb`. pub fn path>(mut self, path: impl Into>) -> Self { self.path = path.into().map(A::into); self } pub fn position(mut self, position: impl Into>) -> Self { self.position = position.into(); self } pub fn line(mut self, line: impl Into>) -> Self { self.line = line.into(); self } } #[cfg(test)] mod tests { #[tokio::test] async fn serialize() { let octocrab = crate::Octocrab::default(); let handler = octocrab.commits("owner", "repo"); let list = handler .create_comment("95b3b039e71659a401ef39e86bab691ab6ce5fe5", "boo boo") .path("lib/octocat.rb") .position(10) .line(1); assert_eq!( serde_json::to_value(list).unwrap(), serde_json::json!({ "sha": "95b3b039e71659a401ef39e86bab691ab6ce5fe5", "body": "boo boo", "path": "lib/octocat.rb", "position": 10, "line": 1, }) ) } } octocrab-0.31.2/src/api/commits.rs000064400000000000000000000040411046102023000150630ustar 00000000000000//! The commit API. mod associated_check_runs; mod associated_pull_requests; mod compare_commit; mod create_comment; pub use associated_pull_requests::PullRequestTarget; pub use self::create_comment::CreateCommentBuilder; use crate::params::repos::Reference; use crate::{models, Octocrab, Result}; pub struct CommitHandler<'octo> { crab: &'octo Octocrab, owner: String, repo: String, } impl<'octo> CommitHandler<'octo> { pub(crate) fn new(crab: &'octo Octocrab, owner: String, repo: String) -> Self { Self { crab, owner, repo } } // pub fn create(&self, title: impl Into) -> create::CreateIssueBuilder<'_, '_> { // create::CreateIssueBuilder::new(self, title.into()) // } pub fn compare( &self, base: impl Into, head: impl Into, ) -> compare_commit::CompareCommitsBuilder<'_, '_> { compare_commit::CompareCommitsBuilder::new(self, base.into(), head.into()) } pub fn associated_check_runs( &self, reference: impl Into, ) -> associated_check_runs::AssociatedCheckRunsBuilder<'_, '_> { associated_check_runs::AssociatedCheckRunsBuilder::new(self, reference) } pub fn associated_pull_requests( &self, target: PullRequestTarget, ) -> associated_pull_requests::AssociatedPullRequestsBuilder<'_, '_> { associated_pull_requests::AssociatedPullRequestsBuilder::new(self, target) } pub fn create_comment( &self, sha: impl Into, body: impl Into, ) -> create_comment::CreateCommentBuilder<'_, '_> { create_comment::CreateCommentBuilder::new(self, sha.into(), body.into()) } pub async fn get(&self, reference: impl Into) -> Result { let route = format!( "/repos/{owner}/{repo}/commits/{reference}", owner = self.owner, repo = self.repo, reference = reference.into(), ); self.crab.get(route, None::<&()>).await } } octocrab-0.31.2/src/api/current.rs000064400000000000000000000502561046102023000151030ustar 00000000000000//! Get data about the currently authenticated user. use crate::{ models::{self, gists::Gist, orgs::MembershipInvitation, Installation, Repository}, Octocrab, Page, Result, }; use chrono::{DateTime, Utc}; /// Handler for the current authenication API. **Note** All of the methods /// provided below require at least some authenication such as personal token /// in order to be used. /// /// Created with [`Octocrab::current`]. pub struct CurrentAuthHandler<'octo> { crab: &'octo Octocrab, } impl<'octo> CurrentAuthHandler<'octo> { pub(crate) fn new(crab: &'octo Octocrab) -> Self { Self { crab } } /// Fetches information about the current user. pub async fn user(&self) -> Result { self.crab.get("/user", None::<&()>).await } /// Fetches information about the currently authenticated app. /// /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); /// /// let app = octocrab /// .current() /// .app() /// .await?; /// /// println!("{}", app.name); /// # Ok(()) /// # } /// ``` pub async fn app(&self) -> Result { self.crab.get("/app", None::<&()>).await } /// List repositories starred by current authenticated user. /// /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// octocrab::instance() /// .current() /// .list_repos_starred_by_authenticated_user() /// .send() /// .await?; /// # Ok(()) /// # } /// ``` /// /// [See the GitHub API documentation](https://docs.github.com/en/rest/reference/activity#list-repositories-starred-by-the-authenticated-user) pub fn list_repos_starred_by_authenticated_user(&self) -> ListStarredReposBuilder<'octo> { ListStarredReposBuilder::new(self.crab) } /// Lists repositories that the current authenticated user. /// /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// octocrab::instance() /// .current() /// .list_repos_for_authenticated_user() /// .send() /// .await?; /// # Ok(()) /// # } /// ``` /// /// [See the GitHub API documentation](https://docs.github.com/en/rest/reference/repos#list-repositories-for-the-authenticated-user) pub fn list_repos_for_authenticated_user(&self) -> ListReposForAuthenticatedUserBuilder<'octo> { ListReposForAuthenticatedUserBuilder::new(self.crab) } /// List gists for the current authenticated user. /// /// # Examples /// /// 1. The following snippet retrieves the most recent gist: /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// octocrab::instance() /// .current() /// .list_gists_for_authenticated_user() /// .per_page(1) /// .page(1) /// .send() /// .await?; /// # Ok(()) /// # } /// ``` /// /// 2. This retrieves the first 100 gists, which is maximum number that /// can be fetched in a single page: /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// octocrab::instance() /// .current() /// .list_gists_for_authenticated_user() /// .per_page(100) /// .page(1) /// .send() /// .await?; /// # Ok(()) /// # } /// ``` /// /// [See the GitHub API documentation](https://docs.github.com/en/rest/gists/gists?apiVersion=latest#list-gists-for-the-authenticated-user) pub fn list_gists_for_authenticated_user(&self) -> ListGistsForAuthenticatedUserBuilder<'octo> { // self.crab.get("/gists", None::<&()>).await ListGistsForAuthenticatedUserBuilder::new(self.crab) } /// List gists that were starred by the authenticated user. pub fn list_gists_starred_by_authenticated_user(&self) -> ListStarredGistsBuilder<'octo> { ListStarredGistsBuilder::new(self.crab) } /// Lists installations of your GitHub App that the authenticated user has explicit permission (:read, :write, or :admin) to access. /// /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// octocrab::instance() /// .current() /// .list_app_installations_accessible_to_user() /// .send() /// .await?; /// # Ok(()) /// # } /// ``` /// /// [See the GitHub API documentation](https://docs.github.com/en/rest/apps/installations?apiVersion=2022-11-28#list-app-installations-accessible-to-the-user-access-token) pub fn list_app_installations_accessible_to_user( &self, ) -> ListAppInstallationsAccessibleToUserBuilder<'octo> { ListAppInstallationsAccessibleToUserBuilder::new(self.crab) } /// Lists organizations that the current authenticated user is a member of. /// /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// octocrab::instance() /// .current() /// .list_org_memberships_for_authenticated_user() /// .send() /// .await?; /// # Ok(()) /// # } /// ``` /// /// [See the GitHub API documentation](https://docs.github.com/en/rest/orgs/members#list-organization-memberships-for-the-authenticated-user) pub fn list_org_memberships_for_authenticated_user( &self, ) -> ListOrgMembershipsForAuthenticatedUserBuilder<'octo> { ListOrgMembershipsForAuthenticatedUserBuilder::new(self.crab) } } /// A builder pattern struct for listing starred repositories. /// /// Created by [`CurrentAuthHandler::list_repos_starred_by_authenticated_user`]. /// /// [`CurrentAuthHandler::list_repos_starred_by_authenticated_user`]: ./struct.CurrentAuthHandler.html#method.list_repos_starred_by_authenticated_user #[derive(serde::Serialize)] pub struct ListStarredReposBuilder<'octo> { #[serde(skip)] crab: &'octo Octocrab, #[serde(skip_serializing_if = "Option::is_none")] sort: Option, #[serde(skip_serializing_if = "Option::is_none")] direction: Option, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo> ListStarredReposBuilder<'octo> { fn new(crab: &'octo Octocrab) -> Self { Self { crab, sort: None, direction: None, per_page: None, page: None, } } /// One of `created` (when the repository was starred) or `updated` (when it was last pushed to). /// /// [See the GitHub API documentation](https://docs.github.com/en/rest/reference/activity#list-repositories-starred-by-the-authenticated-user--parameters) pub fn sort(mut self, sort: impl Into) -> Self { self.sort = Some(sort.into()); self } /// One of `asc` (ascending) or `desc` (descending). /// /// [See the GitHub API documentation](https://docs.github.com/en/rest/reference/activity#list-repositories-starred-by-the-authenticated-user--parameters) pub fn direction(mut self, direction: impl Into) -> Self { self.direction = Some(direction.into()); self } /// Results per page (max 100). /// /// [See the GitHub API documentation](https://docs.github.com/en/rest/reference/activity#list-repositories-starred-by-the-authenticated-user--parameters) pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. /// /// [See the GitHub API documentation](https://docs.github.com/en/rest/reference/activity#list-repositories-starred-by-the-authenticated-user--parameters) pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Sends the actual request. pub async fn send(self) -> crate::Result> { self.crab.get("/user/starred", Some(&self)).await } } /// A builder pattern struct for listing repositories for authenticated user. /// /// Created by [`CurrentAuthHandler::list_repos_for_authenticated_user`]. /// /// [`CurrentAuthHandler::list_repos_for_authenticated_user`]: ./struct.CurrentAuthHandler.html#method.list_repos_for_authenticated_user #[derive(serde::Serialize)] pub struct ListReposForAuthenticatedUserBuilder<'octo> { #[serde(skip)] crab: &'octo Octocrab, #[serde(skip_serializing_if = "Option::is_none")] visibility: Option, #[serde(skip_serializing_if = "Option::is_none")] affiliation: Option, #[serde(skip_serializing_if = "Option::is_none")] r#type: Option, #[serde(skip_serializing_if = "Option::is_none")] sort: Option, #[serde(skip_serializing_if = "Option::is_none")] direction: Option, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, #[serde(skip_serializing_if = "Option::is_none")] since: Option>, #[serde(skip_serializing_if = "Option::is_none")] before: Option>, } impl<'octo> ListReposForAuthenticatedUserBuilder<'octo> { fn new(crab: &'octo Octocrab) -> Self { Self { crab, visibility: None, affiliation: None, r#type: None, sort: None, direction: None, per_page: None, page: None, since: None, before: None, } } /// Can be one of `all`, `public`, or `private`. Note: For GitHub AE, can be one of `all`, `internal`, or `private`. /// /// [See the GitHub API documentation](https://docs.github.com/en/rest/reference/repos#list-repositories-for-the-authenticated-user--parameters) pub fn visibility(mut self, visibility: impl Into) -> Self { self.visibility = Some(visibility.into()); self } /// Comma-separated list of values. Can include: /// * `owner`: Repositories that are owned by the authenticated user. /// * `collaborator`: Repositories that the user has been added to as a collaborator. /// * `organization_member`: Repositories that the user has access to through being a member of an organization. This includes every repository on every team that the user is on. /// /// [See the GitHub API documentation](https://docs.github.com/en/rest/reference/repos#list-repositories-for-the-authenticated-user--parameters) pub fn affiliation(mut self, affiliation: impl Into) -> Self { self.affiliation = Some(affiliation.into()); self } /// Can be one of `all`, `owner`, `public`, `private`, `member`. /// /// Note: For GitHub AE, can be one of `all`, `owner`, `internal`, `private`, `member`. /// /// [See the GitHub API documentation](https://docs.github.com/en/rest/reference/repos#list-repositories-for-the-authenticated-user--parameters) pub fn type_(mut self, type_: impl Into) -> Self { self.r#type = Some(type_.into()); self } /// Can be one of `created`, `updated`, `pushed`, `full_name`. /// /// [See the GitHub API documentation](https://docs.github.com/en/rest/reference/repos#list-repositories-for-the-authenticated-user--parameters) pub fn sort(mut self, sort: impl Into) -> Self { self.sort = Some(sort.into()); self } /// Can be one of `asc` or `desc`. /// /// [See the GitHub API documentation](https://docs.github.com/en/rest/reference/repos#list-repositories-for-the-authenticated-user--parameters) pub fn direction(mut self, direction: impl Into) -> Self { self.direction = Some(direction.into()); self } /// Results per page (max 100). /// /// [See the GitHub API documentation](https://docs.github.com/en/rest/reference/repos#list-repositories-for-the-authenticated-user--parameters) pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. /// /// [See the GitHub API documentation](https://docs.github.com/en/rest/reference/repos#list-repositories-for-the-authenticated-user--parameters) pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Only show notifications updated after the given time. /// /// [See the GitHub API documentation](https://docs.github.com/en/rest/reference/repos#list-repositories-for-the-authenticated-user--parameters) pub fn since(mut self, since: impl Into>) -> Self { self.since = Some(since.into()); self } /// Only show notifications updated before the given time. /// /// [See the GitHub API documentation](https://docs.github.com/en/rest/reference/repos#list-repositories-for-the-authenticated-user--parameters) pub fn before(mut self, before: impl Into>) -> Self { self.before = Some(before.into()); self } /// Sends the actual request. pub async fn send(self) -> crate::Result> { self.crab.get("/user/repos", (&self).into()).await } } /// A builder struct for initializing query parameters for use with the /// `/gists` endpoint. /// /// Created by: [`CurrentAuthHandler::list_gists_for_authenticated_user`]. /// /// [`CurrentAuthHandler::list_repos_starred_by_authenticated_user`]: ./struct.CurrentAuthHandler.html#method.list_gists_for_authenticated_user #[derive(serde::Serialize)] pub struct ListGistsForAuthenticatedUserBuilder<'octo> { /// Client under use for building the request. #[serde(skip)] crab: &'octo Octocrab, /// Only show gists that were updated after the given ISO 8601 UTC timestamp. #[serde(skip_serializing_if = "Option::is_none")] since: Option>, /// The number of results per page (max 100). #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, /// Page number of the results to fetch, starting at 1. #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo> ListGistsForAuthenticatedUserBuilder<'octo> { /// Create a new builder using the given client and default options as /// described in GitHub's API docs. /// /// [See the GitHub API documentation](https://docs.github.com/en/rest/gists/gists?apiVersion=latest#list-gists-for-the-authenticated-user) pub fn new(crab: &'octo Octocrab) -> Self { Self { crab, since: None, per_page: None, page: None, } } /// Only show gists that were updated after the given ISO 8601 UTC timestamp. pub fn since(mut self, last_updated: DateTime) -> Self { self.since = Some(last_updated); self } /// The number of results per page (max 100). pub fn per_page(mut self, count: u8) -> Self { self.per_page = Some(count); self } /// Page number of the results to fetch, starting at 1. pub fn page(mut self, page_num: u32) -> Self { self.page = Some(page_num); self } /// Sends the actual request. pub async fn send(self) -> crate::Result> { self.crab.get("/gists", Some(&self)).await } } #[derive(serde::Serialize)] pub struct ListStarredGistsBuilder<'octo> { /// Client under use for building the request. #[serde(skip)] crab: &'octo Octocrab, /// Only show gists that were starred after the given ISO 8601 UTC timestamp. #[serde(skip_serializing_if = "Option::is_none")] since: Option>, /// Number of results to return per page. Maximum supported value is `100`. /// Larger values are clamped to `100`. Defaults to `30` #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, /// Page number of the results to fetch. Defaults to `1`. #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo> ListStarredGistsBuilder<'octo> { pub fn new(crab: &'octo Octocrab) -> Self { Self { crab, since: None, per_page: None, page: None, } } /// Only show gists that were starred after the given ISO 8601 UTC timestamp. pub fn since(mut self, last_updated: DateTime) -> Self { self.since = Some(last_updated); self } /// The page number from the result set to fetch. pub fn page(mut self, page_num: u32) -> Self { self.page = Some(page_num); self } pub fn per_page(mut self, count: u8) -> Self { self.per_page = Some(count); self } /// Sends the actual request. pub async fn send(self) -> crate::Result> { self.crab.get("/gists/starred", Some(&self)).await } } /// A builder pattern struct for listing organizations the authenticated user is a member of. /// /// Created by [`CurrentAuthHandler::list_org_memberships_for_authenticated_user`]. /// /// [`CurrentAuthHandler::list_org_memberships_for_authenticated_user`]: ./struct.CurrentAuthHandler.html#method.list_org_memberships_for_authenticated_user #[derive(serde::Serialize)] pub struct ListOrgMembershipsForAuthenticatedUserBuilder<'octo> { #[serde(skip)] crab: &'octo Octocrab, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo> ListOrgMembershipsForAuthenticatedUserBuilder<'octo> { fn new(crab: &'octo Octocrab) -> Self { Self { crab, per_page: None, page: None, } } /// Results per page (max 100). /// /// [See the GitHub API documentation](https://docs.github.com/en/rest/orgs/members#list-organization-memberships-for-the-authenticated-user--parameters) pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. /// /// [See the GitHub API documentation](https://docs.github.com/en/rest/orgs/members#list-organization-memberships-for-the-authenticated-user--parameters) pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Sends the actual request. pub async fn send(self) -> crate::Result> { self.crab .get("/user/memberships/orgs", (&self).into()) .await } } /// A builder pattern struct for listing the installations accessible to a user access token. /// /// Created by [`CurrentAuthHandler::list_app_installations_accessible_to_user`]. /// /// [`CurrentAuthHandler::list_app_installations_accessible_to_user`]: ./struct.CurrentAuthHandler.html#method.list_app_installations_accessible_to_user #[derive(serde::Serialize)] pub struct ListAppInstallationsAccessibleToUserBuilder<'octo> { #[serde(skip)] crab: &'octo Octocrab, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo> ListAppInstallationsAccessibleToUserBuilder<'octo> { fn new(crab: &'octo Octocrab) -> Self { Self { crab, per_page: None, page: None, } } /// Results per page (max 100). /// /// [See the GitHub API documentation](https://docs.github.com/en/rest/apps/installations?apiVersion=2022-11-28#list-app-installations-accessible-to-the-user-access-token--parameters) pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. /// /// [See the GitHub API documentation](https://docs.github.com/en/rest/apps/installations?apiVersion=2022-11-28#list-app-installations-accessible-to-the-user-access-token--parameters) pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Sends the actual request. pub async fn send(self) -> crate::Result> { self.crab.get("/user/installations", (&self).into()).await } } octocrab-0.31.2/src/api/events.rs000064400000000000000000000046711046102023000147250ustar 00000000000000//! GitHub Events use crate::{ etag::{EntityTag, Etagged}, models::events, FromResponse, Octocrab, Page, }; use http::request::Builder; use http::{header::HeaderMap, Method, StatusCode}; pub struct EventsBuilder<'octo> { crab: &'octo Octocrab, headers: Headers, params: Params, } struct Headers { etag: Option, } #[derive(serde::Serialize)] struct Params { #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo> EventsBuilder<'octo> { pub(crate) fn new(crab: &'octo Octocrab) -> Self { Self { crab, headers: Headers { etag: None }, params: Params { per_page: None, page: None, }, } } /// Etag for this request. pub fn etag(mut self, etag: Option) -> Self { self.headers.etag = etag; self } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.params.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.params.page = Some(page.into()); self } /// Sends the actual request. pub async fn send(self) -> crate::Result>> { let route = "/events".to_string(); let uri = self.crab.parameterized_uri(route, Some(&self.params))?; let mut headers = HeaderMap::new(); if let Some(etag) = self.headers.etag { EntityTag::insert_if_none_match_header(&mut headers, etag)?; } let mut builder = Builder::new().method(Method::GET).uri(uri); for (key, value) in headers.iter() { builder = builder.header(key, value); } let request = self.crab.build_request(builder, None::<&()>)?; let response = self.crab.execute(request).await?; let etag = EntityTag::extract_from_response(&response); if response.status() == StatusCode::NOT_MODIFIED { Ok(Etagged { etag, value: None }) } else { >::from_response(crate::map_github_error(response).await?) .await .map(|page| Etagged { etag, value: Some(page), }) } } } octocrab-0.31.2/src/api/gists/list_commits.rs000064400000000000000000000022631046102023000172530ustar 00000000000000use super::*; #[derive(serde::Serialize)] pub struct ListCommitsBuilder<'octo, 'b> { #[serde(skip)] handler: &'b GistsHandler<'octo>, #[serde(skip)] gist_id: String, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo, 'b> ListCommitsBuilder<'octo, 'b> { pub(crate) fn new(handler: &'b GistsHandler<'octo>, gist_id: String) -> Self { Self { handler, gist_id, per_page: None, page: None, } } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Sends the actual request. pub async fn send(self) -> crate::Result> { let route = format!("/gists/{gist_id}/commits", gist_id = self.gist_id); self.handler.crab.get(route, Some(&self)).await } } octocrab-0.31.2/src/api/gists/list_forks.rs000064400000000000000000000030561046102023000167250ustar 00000000000000use crate::{gists::GistsHandler, models::gists::Gist, Page, Result}; use serde; #[derive(serde::Serialize)] pub struct ListGistForksBuilder<'octo, 'b> { #[serde(skip)] handler: &'b GistsHandler<'octo>, #[serde(skip)] gist_id: String, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo, 'b> ListGistForksBuilder<'octo, 'b> { pub(crate) fn new(handler: &'b GistsHandler<'octo>, gist_id: String) -> Self { Self { handler, gist_id, per_page: None, page: None, } } /// Set the `per_page` query parameter on the builder. /// /// Controls the number of results to return per "page" of results. /// The maximum value is 100 results per page retrieved. Values larger than /// `100` are clamped to `100` by GitHub's API pub fn per_page(mut self, count: u8) -> Self { self.per_page = Some(count); self } /// Sets the `page` query parameter on the builder. /// /// Controls which page of the result set should be retrieved. /// All pages are retrieved if this is omitted. pub fn page(mut self, page_num: u32) -> Self { self.page = Some(page_num); self } /// Sends the actual request to GitHub's API pub async fn send(self) -> Result> { let route = format!("/gists/{gist_id}/forks", gist_id = self.gist_id); self.handler.crab.get(route, Some(&self)).await } } octocrab-0.31.2/src/api/gists/list_gists.rs000064400000000000000000000074261046102023000167370ustar 00000000000000use std::marker::PhantomData; use super::*; use chrono::{DateTime, Utc}; pub trait EndpointSelector { const ENDPOINT: &'static str; } pub struct AllOrByAuth; pub struct PublicOnly; impl EndpointSelector for AllOrByAuth { const ENDPOINT: &'static str = "/gists"; } impl EndpointSelector for PublicOnly { const ENDPOINT: &'static str = "/gists/public"; } #[derive(Debug, serde::Serialize)] pub struct ListGistsBuilder<'octo, T: EndpointSelector> { #[serde(skip)] visibility_type: PhantomData, #[serde(skip)] crab: &'octo Octocrab, /// Only show gists that were created after this UTC timestamp. #[serde(skip_serializing_if = "Option::is_none")] since: Option>, /// The maximum number of results in each page retrieved. #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, /// The page number to fetch. This starts at (and defaults to) 1 #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo, T: EndpointSelector> ListGistsBuilder<'octo, T> { pub fn new(crab: &'octo Octocrab) -> Self { Self { visibility_type: PhantomData, crab, since: None, per_page: None, page: None, } } /// Only show gists that were created after this UTC timestamp. pub fn since(mut self, created_after: impl Into>) -> Self { self.since = Some(created_after.into()); self } /// The maximum number of results in each page retrieved. pub fn per_page(mut self, count: u8) -> Self { self.per_page = Some(count); self } /// The page number to fetch. This starts at (and defaults to) 1 pub fn page(mut self, number: u32) -> Self { self.page = Some(number); self } /// Sends the actual request. pub async fn send(self) -> crate::Result> { self.crab.get(T::ENDPOINT, Some(&self)).await } } /// Handles query data for the `GET /gists` endpoint. /// /// This endpoint has differing behaviour depending on the status of /// authentication. pub type ListAllGistsBuilder<'octo> = ListGistsBuilder<'octo, AllOrByAuth>; /// Handles query data for the `GET /gists/public` endpoint. /// /// Fetches all publicly available gists on the GitHub instance with pagination. pub type ListPublicGistsBuilder<'octo> = ListGistsBuilder<'octo, PublicOnly>; /// Handles query data for the `GET /users/{username}/gists` endpoint. #[derive(Debug, serde::Serialize)] pub struct ListUserGistsBuilder<'octo> { #[serde(skip)] crab: &'octo Octocrab, #[serde(skip)] /// Username for which to retrieve gists username: String, #[serde(skip_serializing_if = "Option::is_none")] since: Option>, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo> ListUserGistsBuilder<'octo> { pub fn new(crab: &'octo Octocrab, username: String) -> Self { Self { crab, username, since: None, per_page: None, page: None, } } pub fn since(mut self, last_updated: DateTime) -> Self { self.since = Some(last_updated); self } pub fn per_page(mut self, count: u8) -> Self { self.per_page = Some(count); self } pub fn page(mut self, number: u32) -> Self { self.page = Some(number); self } pub async fn send(self) -> crate::Result> { self.crab .get( format!("/users/{username}/gists", username = self.username), Some(&self), ) .await } } octocrab-0.31.2/src/api/gists.rs000064400000000000000000000406771046102023000145600ustar 00000000000000//! The gist API //! //! Supports CRUD operations on gists in GitHub. //! //! [Official documentation][docs] //! //! [docs]: https://docs.github.com/en/rest/gists/gists?apiVersion=2022-11-28 mod list_commits; mod list_forks; mod list_gists; use http::StatusCode; use serde::Serialize; use std::collections::BTreeMap; pub use self::list_commits::ListCommitsBuilder; pub use self::list_gists::{ListAllGistsBuilder, ListPublicGistsBuilder, ListUserGistsBuilder}; use crate::{ models::gists::{Gist, GistRevision}, Octocrab, Result, }; /// Handler for GitHub's gist API. /// /// Created with [`Octocrab::gists`]. pub struct GistsHandler<'octo> { crab: &'octo Octocrab, } impl<'octo> GistsHandler<'octo> { pub(crate) fn new(crab: &'octo Octocrab) -> Self { Self { crab } } /// List all gists from GitHub's gist API. /// /// See: [GitHub API Documentation][docs] for `GET /gists` /// /// # Note /// * Calling with an authentication token will list all the gists of the /// authenticated user /// /// * If no authentication token will list all the public gists from /// GitHub's API. This can potentially produce a lot of results, so care is /// advised. /// /// # Example /// /// 1) This shows one page of (10) results for all public gists created a /// from the day before: /// /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let yesterday: chrono::DateTime = /// chrono::Utc::now() /// .checked_sub_days(chrono::Days::new(1)).unwrap(); /// octocrab::instance() /// .gists() /// .list_all_gists() /// .since(yesterday) /// .page(1u32) /// .per_page(10u8) /// .send() /// .await?; /// # Ok(()) /// # } /// ``` /// /// [docs]: https://docs.github.com/en/rest/gists/gists?apiVersion=2022-11-28#list-gists-for-the-authenticated-user pub fn list_all_gists(&self) -> ListAllGistsBuilder<'octo> { ListAllGistsBuilder::new(self.crab) } /// List public gists sorted by most recently updated to least recently /// updated. This works similarly to the `GistsHandler::list_all_gists` /// /// See: [GitHub API Documentation][docs] for `GET /gists/public` /// /// # Example /// /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let yesterday: chrono::DateTime = /// chrono::Utc::now() /// .checked_sub_days(chrono::Days::new(1)).unwrap(); /// let all_public_gists = octocrab::instance() /// .gists() /// .list_all_recent_public_gists() /// .since(yesterday) /// .page(1u32) /// .per_page(10u8) /// .send() /// .await?; /// # Ok(()) /// # } /// /// ``` /// /// [docs]: https://docs.github.com/en/rest/gists/gists?apiVersion=2022-11-28#list-public-gists pub fn list_all_recent_public_gists(&self) -> ListPublicGistsBuilder<'octo> { ListPublicGistsBuilder::new(self.crab) } /// List gists for the given username, allowing for pagination. /// /// See [GitHub API Documentation][docs] for details on `GET /users/{username}/gists` /// /// # Examples /// /// * Fetch 10 recent gists for the user with login "foouser": /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// octocrab::instance() /// .gists() /// .list_user_gists("foouser") /// .page(1u32) /// .per_page(10u8) /// .send() /// .await?; /// # Ok(()) /// # } /// ``` /// /// [docs]: https://docs.github.com/en/rest/gists/gists?apiVersion=2022-11-28#list-gists-for-a-user pub fn list_user_gists(&self, username: impl AsRef) -> ListUserGistsBuilder<'octo> { ListUserGistsBuilder::new(self.crab, username.as_ref().to_string()) } /// Create a new gist. /// /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let gitignore = octocrab::instance() /// .gists() /// .create() /// .file("hello_world.rs", "fn main() {\n println!(\"Hello World!\");\n}") /// // Optional Parameters /// .description("Hello World in Rust") /// .public(false) /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn create(&self) -> CreateGistBuilder<'octo> { CreateGistBuilder::new(self.crab) } /// Update an existing gist. /// /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let gitignore = octocrab::instance() /// .gists() /// .update("aa5a315d61ae9438b18d") /// // Optional Parameters /// .description("Updated!") /// .file("hello_world.rs") /// .rename_to("fibonacci.rs") /// .with_content("fn main() {\n println!(\"I should be a Fibonacci!\");\n}") /// .file("delete_me.rs") /// .delete() /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn update(&self, id: impl AsRef) -> UpdateGistBuilder<'octo> { UpdateGistBuilder::new(self.crab, format!("/gists/{id}", id = id.as_ref())) } /// Get a single gist. /// /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let gist = octocrab::instance().gists().get("00000000000000000000000000000000").await?; /// # Ok(()) /// # } /// ``` pub async fn get(&self, id: impl AsRef) -> Result { let id = id.as_ref(); self.crab.get(format!("/gists/{id}"), None::<&()>).await } /// Delete a single gist. /// /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// octocrab::instance().gists().delete("00000000000000000000000000000000").await?; /// # Ok(()) /// # } /// ``` pub async fn delete(&self, gist_id: impl AsRef) -> Result<()> { let gist_id = gist_id.as_ref(); self.crab ._delete(format!("/gists/{gist_id}"), None::<&()>) .await .map(|_| ()) } /// Get a single gist revision. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let revision = octocrab::instance() /// .gists() /// .get_revision("00000000000000000000000000000000", "1111111111111111111111111111111111111111") /// .await?; /// # Ok(()) /// # } /// ``` pub async fn get_revision( &self, id: impl AsRef, sha1: impl AsRef, ) -> Result { let id = id.as_ref(); let sha1 = sha1.as_ref(); self.crab .get(format!("/gists/{id}/{sha1}"), None::<&()>) .await } /// List commits for the specified gist. /// /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// use octocrab::params; /// /// // Get the least active repos belonging to `owner`. /// let page = octocrab::instance() /// .gists() /// .list_commits("00000000000000000000000000000000") /// // Optional Parameters /// .per_page(25) /// .page(5u32) /// // Send the request. /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn list_commits(&self, gist_id: impl Into) -> list_commits::ListCommitsBuilder { list_commits::ListCommitsBuilder::new(self, gist_id.into()) } /// Check if the given is gist is already starred by the authenticated user. /// See [GitHub API Documentation][docs] more information about response /// data. /// /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let is_starred: bool = octocrab::instance() /// .gists() /// .is_starred("00000000000000000000000000000000") /// .await?; /// # Ok(()) /// # } /// ``` /// /// [docs]: https://docs.github.com/en/rest/gists/gists?apiVersion=2022-11-28#check-if-a-gist-is-starred pub async fn is_starred(&self, gist_id: impl AsRef) -> Result { let gist_id = gist_id.as_ref(); let response = self.crab._get(format!("/gists/{gist_id}/star")).await?; // Gist API returns 204 (NO CONTENT) if a gist is starred Ok(response.status() == StatusCode::NO_CONTENT) } /// Star the given gist. See [GitHub API Documentation][docs] more /// information about response data. /// /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// octocrab::instance() /// .gists() /// .star("00000000000000000000000000000000") /// .await?; /// # Ok(()) /// # } /// ``` /// /// [docs]: https://docs.github.com/en/rest/gists/gists?apiVersion=2022-11-28#star-a-gist pub async fn star(&self, gist_id: impl AsRef) -> Result<()> { let gist_id = gist_id.as_ref(); // PUT here returns an empty body, ignore it since it doesn't make // sense to deserialize it as JSON. self.crab ._put(format!("/gists/{gist_id}/star"), None::<&()>) .await .map(|_| ()) } /// Unstar the given gist. See [GitHub API Documentation][docs] more /// information about response data. /// /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// octocrab::instance() /// .gists() /// .unstar("00000000000000000000000000000000") /// .await?; /// # Ok(()) /// # } /// ``` /// /// [docs]: https://docs.github.com/en/rest/gists/gists?apiVersion=2022-11-28#unstar-a-gist pub async fn unstar(&self, gist_id: impl AsRef) -> Result<()> { let gist_id = gist_id.as_ref(); // DELETE here returns an empty body, ignore it since it doesn't make // sense to deserialize it as JSON. self.crab ._delete(format!("/gists/{gist_id}/star"), None::<&()>) .await .map(|_| ()) } /// Retrieve all the gists that forked the given `gist_id`. See /// [GitHub API Docs][docs] for information about request parameters, and /// response schema. /// /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// octocrab::instance() /// .gists() /// .list_forks("00000000000000000000000000000000") /// .send() /// .await?; /// # Ok(()) /// # } /// ``` /// /// [docs]: https://docs.github.com/en/rest/gists/gists?apiVersion=2022-11-28#list-gist-forks pub fn list_forks(&self, gist_id: impl Into) -> list_forks::ListGistForksBuilder { list_forks::ListGistForksBuilder::new(self, gist_id.into()) } /// Create a fork of the given `gist_id` associated with the authenticated /// user's account. See [GitHub API docs][docs] for more information about /// request parameters and response schema. /// /// [docs]: https://docs.github.com/en/rest/gists/gists?apiVersion=2022-11-28#fork-a-gist pub async fn fork(&self, gist_id: impl AsRef) -> Result { let route = format!("/gists/{gist_id}/forks", gist_id = gist_id.as_ref()); self.crab.post(route, None::<&()>).await } } #[derive(Debug)] pub struct CreateGistBuilder<'octo> { crab: &'octo Octocrab, data: CreateGist, } impl<'octo> CreateGistBuilder<'octo> { pub(crate) fn new(crab: &'octo Octocrab) -> Self { Self { crab, data: Default::default(), } } /// Set a description for the gist to be created. pub fn description(mut self, description: impl Into) -> Self { self.data.description = Some(description.into()); self } /// Set the `public` flag of the gist to be created. pub fn public(mut self, public: bool) -> Self { self.data.public = Some(public); self } /// Add a file to the gist with `filename` and `content`. pub fn file(mut self, filename: impl Into, content: impl Into) -> Self { let file = CreateGistFile { filename: Default::default(), content: content.into(), }; self.data.files.insert(filename.into(), file); self } /// Send the `CreateGist` request to Github for execution. pub async fn send(self) -> Result { self.crab.post("/gists", Some(&self.data)).await } } #[derive(Debug, Default, Serialize)] struct CreateGist { #[serde(skip_serializing_if = "Option::is_none")] description: Option, #[serde(skip_serializing_if = "Option::is_none")] public: Option, files: BTreeMap, } #[derive(Debug, Serialize)] struct CreateGistFile { #[serde(skip_serializing_if = "Option::is_none")] filename: Option, content: String, } #[derive(Debug)] pub struct UpdateGistBuilder<'octo> { crab: &'octo Octocrab, gist_path: String, data: UpdateGist, } impl<'octo> UpdateGistBuilder<'octo> { fn new(crab: &'octo Octocrab, gist_path: String) -> Self { Self { crab, gist_path, data: Default::default(), } } /// Update the description of the the gist with the content provided by `description`. pub fn description(mut self, description: impl Into) -> Self { self.data.description = Some(description.into()); self } /// Update the file with the `filename`. /// /// The update operation is chosen in further calls to the returned builder. pub fn file(self, filename: impl Into) -> UpdateGistFileBuilder<'octo> { UpdateGistFileBuilder::new(self, filename) } /// Send the `UpdateGist` command to Github for execution. pub async fn send(self) -> Result { self.crab.patch(self.gist_path, Some(&self.data)).await } } #[derive(Debug, Default, Serialize)] struct UpdateGist { #[serde(skip_serializing_if = "Option::is_none")] description: Option, #[serde(skip_serializing_if = "Option::is_none")] files: Option>>, } #[derive(Debug, Default, Serialize)] pub struct UpdateGistFile { #[serde(skip_serializing_if = "Option::is_none")] filename: Option, #[serde(skip_serializing_if = "Option::is_none")] content: Option, } pub struct UpdateGistFileBuilder<'octo> { builder: UpdateGistBuilder<'octo>, filename: String, file: Option, ready: bool, } impl<'octo> UpdateGistFileBuilder<'octo> { fn new(builder: UpdateGistBuilder<'octo>, filename: impl Into) -> Self { Self { builder, filename: filename.into(), file: None, ready: false, } } fn build(mut self) -> UpdateGistBuilder<'octo> { if self.ready { self.builder .data .files .get_or_insert_with(BTreeMap::new) .insert(self.filename, self.file); } self.builder } /// Delete the file from the gist. pub fn delete(mut self) -> UpdateGistBuilder<'octo> { self.ready = true; self.file = None; self.build() } /// Rename the file to `filename`. pub fn rename_to(mut self, filename: impl Into) -> Self { self.ready = true; self.file.get_or_insert_with(Default::default).filename = Some(filename.into()); self } /// Update the content of the file and overwrite it with `content`. pub fn with_content(mut self, content: impl Into) -> Self { self.ready = true; self.file.get_or_insert_with(Default::default).content = Some(content.into()); self } /// Overwrite the Description of the gist with `description`. /// /// This will finalize the update operation and will continue to operate on the gist itself. pub fn description(self, description: impl Into) -> UpdateGistBuilder<'octo> { self.build().description(description) } /// Update the next file identified by `filename`. /// /// This will finalize the update operation and will continue to operate on the gist itself. pub fn file(self, filename: impl Into) -> UpdateGistFileBuilder<'octo> { self.build().file(filename) } /// Send the `UpdateGist` command to Github for execution. /// /// This will finalize the update operation before sending. pub async fn send(self) -> Result { self.build().send().await } } octocrab-0.31.2/src/api/gitignore.rs000064400000000000000000000032001046102023000153730ustar 00000000000000//! The gitignore API use crate::error::HttpSnafu; use http::{request, Uri}; use snafu::ResultExt; use crate::Octocrab; /// Handler for GitHub's gitignore API. /// /// Created with [`Octocrab::gitignore`]. pub struct GitignoreHandler<'octo> { crab: &'octo Octocrab, } impl<'octo> GitignoreHandler<'octo> { pub(crate) fn new(crab: &'octo Octocrab) -> Self { Self { crab } } /// List all templates available to pass as an option when creating a /// repository. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let list = octocrab::instance().gitignore().list().await?; /// # Ok(()) /// # } /// ``` pub async fn list(&self) -> crate::Result> { self.crab.get("/gitignore/templates", None::<&()>).await } /// Get the source of a single template. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let gitignore = octocrab::instance().gitignore().get("C").await?; /// # Ok(()) /// # } /// ``` pub async fn get(&self, name: impl AsRef) -> crate::Result { let route = format!("/gitignore/templates/{name}", name = name.as_ref()); let uri = Uri::builder() .path_and_query(route) .build() .context(HttpSnafu)?; let mut request = request::Builder::new().method("GET").uri(uri); request = request.header(http::header::ACCEPT, crate::format_media_type("raw")); let request = self.crab.build_request(request, None::<&()>)?; let response = self.crab.execute(request).await?; self.crab.body_to_string(response).await } } octocrab-0.31.2/src/api/issues/create.rs000064400000000000000000000060261046102023000161730ustar 00000000000000use super::*; #[derive(serde::Serialize)] pub struct CreateIssueBuilder<'octo, 'r> { #[serde(skip)] handler: &'r super::IssueHandler<'octo>, title: String, #[serde(skip_serializing_if = "Option::is_none")] body: Option, #[serde(skip_serializing_if = "Option::is_none")] milestone: Option, #[serde(skip_serializing_if = "Option::is_none")] labels: Option>, #[serde(skip_serializing_if = "Option::is_none")] assignees: Option>, } impl<'octo, 'r> CreateIssueBuilder<'octo, 'r> { pub(crate) fn new(handler: &'r super::IssueHandler<'octo>, title: String) -> Self { Self { handler, title, body: None, milestone: None, labels: None, assignees: None, } } /// Sends the actual request. pub async fn send(self) -> crate::Result { let route = format!( "/repos/{owner}/{repo}/issues", owner = self.handler.owner, repo = self.handler.repo, ); self.handler.crab.post(route, Some(&self)).await } /// The contents of the issue. pub fn body>(mut self, body: impl Into>) -> Self { self.body = body.into().map(A::into); self } /// The number of the milestone to associate this issue with. *NOTE: Only /// users with push access can set the milestone for new issues. The /// milestone is silently dropped otherwise.* pub fn milestone(mut self, milestone: impl Into>) -> Self { self.milestone = milestone.into(); self } /// Labels to associate with this issue. *NOTE: Only users with push access /// can set labels for new issues. Labels are silently dropped otherwise.* pub fn labels(mut self, labels: impl Into>>) -> Self { self.labels = labels.into(); self } /// Logins for Users to assign to this issue. *NOTE: Only users with push /// access can set assignees for new issues. Assignees are silently /// dropped otherwise.* pub fn assignees(mut self, assignees: impl Into>>) -> Self { self.assignees = assignees.into(); self } } #[cfg(test)] mod tests { #[tokio::test] async fn serialize() { let octocrab = crate::Octocrab::default(); let handler = octocrab.issues("owner", "repo"); let list = handler .create("test-issue") .body(String::from("testing...")) .milestone(3456) .labels(vec![String::from("help-wanted")]) .assignees(vec![String::from("octocrab"), String::from("ferris")]); assert_eq!( serde_json::to_value(list).unwrap(), serde_json::json!({ "title": "test-issue", "body": "testing...", "milestone": 3456, "labels": ["help-wanted"], "assignees": ["octocrab", "ferris"], }) ) } } octocrab-0.31.2/src/api/issues/list.rs000064400000000000000000000141221046102023000156770ustar 00000000000000use super::*; #[derive(serde::Serialize)] pub struct ListIssuesBuilder<'octo, 'b, 'c, 'd> { #[serde(skip)] handler: &'b IssueHandler<'octo>, #[serde(skip_serializing_if = "Option::is_none")] state: Option, #[serde(skip_serializing_if = "Option::is_none")] milestone: Option>, #[serde(skip_serializing_if = "Option::is_none")] assignee: Option>, #[serde(skip_serializing_if = "Option::is_none")] creator: Option, #[serde(skip_serializing_if = "Option::is_none")] mentioned: Option, #[serde(skip_serializing_if = "Option::is_none")] #[serde(serialize_with = "comma_separated")] labels: Option<&'d [String]>, #[serde(skip_serializing_if = "Option::is_none")] sort: Option, #[serde(skip_serializing_if = "Option::is_none")] direction: Option, #[serde(skip_serializing_if = "Option::is_none")] since: Option>, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo, 'b, 'c, 'd> ListIssuesBuilder<'octo, 'b, 'c, 'd> { pub(crate) fn new(handler: &'b IssueHandler<'octo>) -> Self { Self { handler, state: None, milestone: None, assignee: None, creator: None, mentioned: None, labels: None, sort: None, direction: None, since: None, per_page: None, page: None, } } /// If an integer is passed, it should refer to a milestone by its number /// field. If the string `"*"` is passed, issues with any milestone are /// accepted. If the string none is passed, issues without milestones /// are returned. pub fn milestone(mut self, milestone: impl Into>) -> Self { self.milestone = Some(milestone.into()); self } /// Filter by assignee, can be the name of a user. Pass in the string /// `"none"` for issues with no assigned user, and `"*"` for issues assigned /// to any user. pub fn assignee(mut self, assignee: impl Into>) -> Self { self.assignee = Some(assignee.into()); self } /// Filter by the creator of the issue. pub fn creator(mut self, creator: impl Into) -> Self { self.creator = Some(creator.into()); self } /// Filter by the creator of the issue. pub fn mentioned(mut self, mentioned: impl Into) -> Self { self.mentioned = Some(mentioned.into()); self } /// Filter pull requests by `state`. pub fn state(mut self, state: params::State) -> Self { self.state = Some(state); self } /// Filter issues by label. pub fn labels(mut self, labels: &'d (impl AsRef<[String]> + ?Sized)) -> Self { self.labels = Some(labels.as_ref()); self } /// What to sort results by. Can be either `created`, `updated`, /// `popularity` (comment count) or `long-running` (age, filtering by pulls /// updated in the last month). pub fn sort(mut self, sort: impl Into) -> Self { self.sort = Some(sort.into()); self } /// The direction of the sort. Can be either ascending or descending. /// Default: descending when sort is `created` or sort is not specified, /// otherwise ascending sort. pub fn direction(mut self, direction: impl Into) -> Self { self.direction = Some(direction.into()); self } /// Only return issues updated after the given timestamp. pub fn since(mut self, since: impl Into>) -> Self { self.since = Some(since.into()); self } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Sends the actual request. pub async fn send(self) -> crate::Result> { let route = format!( "/repos/{owner}/{repo}/issues", owner = self.handler.owner, repo = self.handler.repo ); self.handler.crab.get(route, Some(&self)).await } } fn comma_separated( labels: &Option<&[String]>, serializer: S, ) -> Result { serializer.serialize_str(&labels.unwrap().join(",")) } #[cfg(test)] mod tests { #[tokio::test] async fn serialize() { let octocrab = crate::Octocrab::default(); let handler = octocrab.issues("rust-lang", "rust"); let labels = vec![ String::from("help wanted"), String::from("good first issue"), ]; let list = handler .list() .state(crate::params::State::Open) .milestone(1234) .assignee("ferris") .creator("octocrab") .mentioned("octocat") .labels(&labels) .sort(crate::params::issues::Sort::Comments) .direction(crate::params::Direction::Ascending) .since(chrono::DateTime::parse_from_rfc3339("2003-07-01T10:52:37Z").unwrap()) .per_page(100) .page(1u8); assert_eq!( serde_json::to_value(list).unwrap(), serde_json::json!({ "state": "open", "milestone": 1234, "assignee": "ferris", "creator": "octocrab", "mentioned": "octocat", "labels": "help wanted,good first issue", "sort": "comments", "direction": "asc", "since": "2003-07-01T10:52:37Z", "per_page": 100, "page": 1, }) ) } } octocrab-0.31.2/src/api/issues/list_labels.rs000064400000000000000000000046731046102023000172330ustar 00000000000000use super::*; #[derive(serde::Serialize)] pub struct ListLabelsForIssueBuilder<'octo, 'r> { #[serde(skip)] handler: &'r IssueHandler<'octo>, #[serde(skip)] number: u64, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo, 'r> ListLabelsForIssueBuilder<'octo, 'r> { pub(crate) fn new(handler: &'r IssueHandler<'octo>, number: u64) -> Self { Self { handler, number, per_page: None, page: None, } } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Send the actual request. pub async fn send(self) -> Result> { let route = format!( "/repos/{owner}/{repo}/issues/{number}/labels", owner = self.handler.owner, repo = self.handler.repo, number = self.number, ); self.handler.crab.get(route, Some(&self)).await } } #[derive(serde::Serialize)] pub struct ListLabelsForRepoBuilder<'octo, 'r> { #[serde(skip)] handler: &'r IssueHandler<'octo>, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo, 'r> ListLabelsForRepoBuilder<'octo, 'r> { pub(crate) fn new(handler: &'r IssueHandler<'octo>) -> Self { Self { handler, per_page: None, page: None, } } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Send the actual request. pub async fn send(self) -> Result> { let route = format!( "/repos/{owner}/{repo}/labels", owner = self.handler.owner, repo = self.handler.repo, ); self.handler.crab.get(route, Some(&self)).await } } octocrab-0.31.2/src/api/issues/update.rs000064400000000000000000000077221046102023000162160ustar 00000000000000use super::*; #[derive(serde::Serialize)] pub struct UpdateIssueBuilder<'octo, 'a, 'b, 'c, 'd, 'e> { #[serde(skip)] handler: &'a IssueHandler<'octo>, #[serde(skip)] number: u64, #[serde(skip_serializing_if = "Option::is_none")] title: Option<&'b str>, #[serde(skip_serializing_if = "Option::is_none")] body: Option<&'c str>, #[serde(skip_serializing_if = "Option::is_none")] assignees: Option<&'d [String]>, #[serde(skip_serializing_if = "Option::is_none")] state: Option, #[serde(skip_serializing_if = "Option::is_none")] state_reason: Option, #[serde(skip_serializing_if = "Option::is_none")] milestone: Option, #[serde(skip_serializing_if = "Option::is_none")] labels: Option<&'e [String]>, } impl<'octo, 'a, 'b, 'c, 'd, 'e> UpdateIssueBuilder<'octo, 'a, 'b, 'c, 'd, 'e> { pub(crate) fn new(handler: &'a IssueHandler<'octo>, number: u64) -> Self { Self { handler, number, title: None, body: None, assignees: None, state: None, state_reason: None, milestone: None, labels: None, } } /// The title of the issue. pub fn title(mut self, title: &'b (impl AsRef + ?Sized)) -> Self { self.title = Some(title.as_ref()); self } /// The body of the issue. pub fn body(mut self, body: &'c (impl AsRef + ?Sized)) -> Self { self.body = Some(body.as_ref()); self } /// The assignees of the issue. pub fn assignees(mut self, assignees: &'d (impl AsRef<[String]> + ?Sized)) -> Self { self.assignees = Some(assignees.as_ref()); self } /// The state of the issue. pub fn state(mut self, state: impl Into) -> Self { self.state = Some(state.into()); self } /// The state reason of the issue. pub fn state_reason( mut self, state_reason: impl Into, ) -> Self { self.state_reason = Some(state_reason.into()); self } /// The milestone of the issue. pub fn milestone(mut self, milestone: impl Into) -> Self { self.milestone = Some(milestone.into()); self } /// The labels of the issue. pub fn labels(mut self, labels: &'e (impl AsRef<[String]> + ?Sized)) -> Self { self.labels = Some(labels.as_ref()); self } /// Send the actual request. pub async fn send(self) -> Result { let route = format!( "/repos/{owner}/{repo}/issues/{issue}", owner = self.handler.owner, repo = self.handler.repo, issue = self.number, ); self.handler.crab.patch(route, Some(&self)).await } } #[cfg(test)] mod tests { #[tokio::test] async fn serialize() { let octocrab = crate::Octocrab::default(); let handler = octocrab.issues("rust-lang", "rust"); let assignees = &[String::from("ferris")]; let labels = &[ String::from("help wanted"), String::from("good first issue"), ]; let update = handler .update(1234) .title("Updated title") .body("New body") .state(crate::models::IssueState::Closed) .state_reason(crate::models::issues::IssueStateReason::Completed) .milestone(1234u64) .assignees(assignees) .labels(labels); assert_eq!( serde_json::to_value(update).unwrap(), serde_json::json!({ "title": "Updated title", "body": "New body", "state": "closed", "state_reason": "completed", "milestone": 1234, "assignees": ["ferris"], "labels": ["help wanted", "good first issue"], }) ) } } octocrab-0.31.2/src/api/issues.rs000064400000000000000000000772431046102023000147410ustar 00000000000000//! The issue API. mod create; mod list; mod list_labels; mod update; use crate::error::HttpSnafu; use crate::models::{CommentId, ReactionId}; use crate::{models, params, Octocrab, Result}; use http::Uri; use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC}; use snafu::ResultExt; pub use self::{ create::CreateIssueBuilder, list::ListIssuesBuilder, list_labels::{ListLabelsForIssueBuilder, ListLabelsForRepoBuilder}, update::UpdateIssueBuilder, }; /// Handler for GitHub's issue API. /// /// Note: GitHub's REST API v3 considers every pull request an issue, but not /// every issue is a pull request. For this reason, "Issues" endpoints may /// return both issues and pull requests in the response. You can identify pull /// requests by the `pull_request` key. /// /// Created with [`Octocrab::issues`]. pub struct IssueHandler<'octo> { crab: &'octo Octocrab, owner: String, repo: String, } impl<'octo> IssueHandler<'octo> { pub(crate) fn new(crab: &'octo Octocrab, owner: String, repo: String) -> Self { Self { crab, owner, repo } } /// Gets an issue from the repository. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); /// let issue = octocrab.issues("owner", "repo").get(3).await?; /// # Ok(()) /// # } /// ``` pub async fn get(&self, number: u64) -> Result { let route = format!( "/repos/{owner}/{repo}/issues/{number}", owner = self.owner, repo = self.repo, number = number, ); self.crab.get(route, None::<&()>).await } /// Create an issue in the repository. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); /// let issue = octocrab.issues("owner", "repo").create("My first issue") /// // Optional Parameters /// .body("This is an autogenerated issue..") /// .milestone(1001) /// .labels(vec![String::from("help-wanted")]) /// .assignees(vec![String::from("ferris")]) /// // Send the request /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn create(&self, title: impl Into) -> create::CreateIssueBuilder<'_, '_> { create::CreateIssueBuilder::new(self, title.into()) } /// List issues in the repository. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); /// use octocrab::params; /// /// let issue = octocrab.issues("owner", "repo") /// .list() /// // Optional Parameters /// .state(params::State::All) /// .milestone(1234) /// .assignee("ferris") /// .creator("octocrab") /// .mentioned("octocat") /// .labels(&[String::from("help wanted"), String::from("good first issue")]) /// .sort(params::issues::Sort::Comments) /// .direction(params::Direction::Ascending) /// .per_page(100) /// .page(1u8) /// // Send the request /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn list(&self) -> list::ListIssuesBuilder<'_, '_, '_, '_> { list::ListIssuesBuilder::new(self) } /// Update an issue in the repository. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); /// use octocrab::models; /// /// let issue = octocrab.issues("owner", "repo") /// .update(1234u64) /// // Optional Parameters /// .title("Updated title") /// .body("New body") /// .state(models::IssueState::Closed) /// .milestone(1234u64) /// .assignees(&[String::from("ferris")]) /// .labels(&[String::from("help wanted"), String::from("good first issue")]) /// // Send the request /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn update(&self, number: u64) -> update::UpdateIssueBuilder<'_, '_, '_, '_, '_, '_> { update::UpdateIssueBuilder::new(self, number) } /// Users with push access can lock an issue or pull request's conversation. /// /// See also: https://docs.github.com/en/rest/issues/issues#lock-an-issue /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// use octocrab::params; /// /// assert!(octocrab::instance().issues("owner", "repo").lock(404, params::LockReason::OffTopic).await?); /// # Ok(()) /// # } /// ``` pub async fn lock( &self, number: u64, reason: impl Into>, ) -> Result { let route = format!( "/repos/{owner}/{repo}/issues/{number}/lock", owner = self.owner, repo = self.repo, number = number, ); let uri = Uri::builder() .path_and_query(route) .build() .context(HttpSnafu)?; let response = self .crab ._put( uri, reason .into() .map(|reason| { serde_json::json!({ "lock_reason": reason, }) }) .as_ref(), ) .await?; Ok(response.status() == 204) } /// Users with push access can unlock an issue or pull request's conversation. /// /// See also: https://docs.github.com/en/rest/issues/issues#unlock-an-issue /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// assert!(octocrab::instance().issues("owner", "repo").unlock(404).await?); /// # Ok(()) /// # } /// ``` pub async fn unlock(&self, number: u64) -> Result { let route = format!( "/repos/{owner}/{repo}/issues/{number}/lock", owner = self.owner, repo = self.repo, number = number, ); let uri = Uri::builder() .path_and_query(route) .build() .context(HttpSnafu)?; let response = self.crab._delete(uri, None::<&()>).await?; Ok(response.status() == 204) } } /// # Assignees impl<'octo> IssueHandler<'octo> { /// Adds up to 10 assignees to an issue. Users already assigned to an issue /// are not replaced. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); /// let issue = octocrab.issues("owner", "repo").add_assignees(101, &["username1", "username2"]).await?; /// # Ok(()) /// # } /// ``` pub async fn add_assignees( &self, number: u64, assignees: &[&str], ) -> Result { let route = format!( "/repos/{owner}/{repo}/issues/{issue}/assignees", owner = self.owner, repo = self.repo, issue = number ); self.crab .post(route, Some(&serde_json::json!({ "assignees": assignees }))) .await } /// Checks if a user has permission to be assigned to an issue in /// the repository. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); /// assert!(octocrab.issues("owner", "repo").check_assignee("ferris").await?); /// # Ok(()) /// # } /// ``` pub async fn check_assignee(&self, assignee: impl AsRef) -> Result { let route = format!( "/repos/{owner}/{repo}/assignees/{assignee}", owner = self.owner, repo = self.repo, assignee = assignee.as_ref() ); let uri = Uri::builder() .path_and_query(route) .build() .context(HttpSnafu)?; let response = self.crab._get(uri).await?; let status = response.status(); if status == 204 { Ok(true) } else if status == 404 { Ok(false) } else { Err(crate::map_github_error(response).await.unwrap_err()) } } /// Lists the available assignees for issues in a repository. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); /// let assignees = octocrab /// .issues("owner", "repo") /// .list_assignees() /// .per_page(15) /// .page(2u32) /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn list_assignees(&self) -> ListAssigneesBuilder { ListAssigneesBuilder::new(self) } } #[derive(serde::Serialize)] pub struct ListAssigneesBuilder<'octo, 'r> { #[serde(skip)] handler: &'r IssueHandler<'octo>, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo, 'r> ListAssigneesBuilder<'octo, 'r> { pub(crate) fn new(handler: &'r IssueHandler<'octo>) -> Self { Self { handler, per_page: None, page: None, } } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Send the actual request. pub async fn send(self) -> Result> { let route = format!( "/repos/{owner}/{repo}/assignees", owner = self.handler.owner, repo = self.handler.repo, ); self.handler.crab.get(route, Some(&self)).await } } /// # Labels impl<'octo> IssueHandler<'octo> { /// Adds `labels` to an issue. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let labels = octocrab::instance() /// .issues("owner", "repo") /// .add_labels(101, &[String::from("help wanted")]) /// .await?; /// # Ok(()) /// # } /// ``` pub async fn add_labels(&self, number: u64, labels: &[String]) -> Result> { let route = format!( "/repos/{owner}/{repo}/issues/{issue}/labels", owner = self.owner, repo = self.repo, issue = number ); self.crab .post(route, Some(&serde_json::json!({ "labels": labels }))) .await } /// Removes `label` from an issue. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let removed_labels = octocrab::instance() /// .issues("owner", "repo") /// .remove_label(101, "my_label") /// .await?; /// # Ok(()) /// # } /// ``` pub async fn remove_label( &self, number: u64, label: impl AsRef, ) -> Result> { let route = format!( "/repos/{owner}/{repo}/issues/{issue_number}/labels/{name}", owner = self.owner, repo = self.repo, issue_number = number, name = utf8_percent_encode(label.as_ref(), NON_ALPHANUMERIC), ); self.crab.delete(route, None::<&()>).await } /// Replaces all labels for an issue. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let labels = octocrab::instance() /// .issues("owner", "repo") /// .replace_all_labels(101, &[String::from("help wanted")]) /// .await?; /// # Ok(()) /// # } /// ``` pub async fn replace_all_labels( &self, number: u64, labels: &[String], ) -> Result> { let route = format!( "/repos/{owner}/{repo}/issues/{issue}/labels", owner = self.owner, repo = self.repo, issue = number ); self.crab .put(route, Some(&serde_json::json!({ "labels": labels }))) .await } /// Creates a label in the repository. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let label = octocrab::instance() /// .issues("owner", "repo") /// .create_label("help wanted", "59dd5a", "") /// .await?; /// # Ok(()) /// # } /// ``` pub async fn create_label( &self, name: impl AsRef, color: impl AsRef, description: impl AsRef, ) -> Result { let route = format!( "/repos/{owner}/{repo}/labels", owner = self.owner, repo = self.repo, ); self.crab .post( route, Some(&serde_json::json!({ "name": name.as_ref(), "color": color.as_ref(), "description": description.as_ref() })), ) .await } /// Gets a label from the repository. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let label = octocrab::instance() /// .issues("owner", "repo") /// .get_label("help wanted") /// .await?; /// # Ok(()) /// # } /// ``` pub async fn get_label(&self, name: impl AsRef) -> Result { let route = format!( "/repos/{owner}/{repo}/labels/{name}", owner = self.owner, repo = self.repo, name = name.as_ref(), ); self.crab.get(route, None::<&()>).await } /// Deletes a label in the repository. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let label = octocrab::instance() /// .issues("owner", "repo") /// .delete_label("help wanted") /// .await?; /// # Ok(()) /// # } /// ``` pub async fn delete_label(&self, name: impl AsRef) -> Result { let route = format!( "/repos/{owner}/{repo}/labels/{name}", owner = self.owner, repo = self.repo, name = name.as_ref(), ); self.crab.delete(route, None::<&()>).await } /// List labels from an issue on a repository. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let page = octocrab::instance() /// .issues("owner", "repo") /// .list_labels_for_issue(404) /// // Optional Parameters /// .per_page(20) /// .page(2u32) /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn list_labels_for_issue(&self, number: u64) -> ListLabelsForIssueBuilder { ListLabelsForIssueBuilder::new(self, number) } /// List all labels from a repository. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let page = octocrab::instance() /// .issues("owner", "repo") /// .list_labels_for_repo() /// // Optional Parameters /// .per_page(20) /// .page(2u32) /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn list_labels_for_repo(&self) -> ListLabelsForRepoBuilder { ListLabelsForRepoBuilder::new(self) } } /// # Comments impl<'octo> IssueHandler<'octo> { /// Creates a comment in the issue. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let comment = octocrab::instance() /// .issues("owner", "repo") /// .create_comment(101, "Beep Boop") /// .await?; /// # Ok(()) /// # } /// ``` pub async fn create_comment( &self, number: u64, body: impl AsRef, ) -> Result { let route = format!( "/repos/{owner}/{repo}/issues/{issue}/comments", owner = self.owner, repo = self.repo, issue = number ); self.crab .post(route, Some(&serde_json::json!({ "body": body.as_ref() }))) .await } /// Gets a comment in the issue. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let comment = octocrab::instance() /// .issues("owner", "repo") /// .get_comment(101u64.into()) /// .await?; /// # Ok(()) /// # } /// ``` pub async fn get_comment(&self, comment_id: CommentId) -> Result { let route = format!( "/repos/{owner}/{repo}/issues/comments/{comment_id}", owner = self.owner, repo = self.repo, ); self.crab.get(route, None::<&()>).await } /// Updates a comment in the issue. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let comment = octocrab::instance() /// .issues("owner", "repo") /// .update_comment(101u64.into(), "Beep Boop") /// .await?; /// # Ok(()) /// # } /// ``` pub async fn update_comment( &self, comment_id: CommentId, body: impl AsRef, ) -> Result { let route = format!( "/repos/{owner}/{repo}/issues/comments/{comment_id}", owner = self.owner, repo = self.repo, ); self.crab .post(route, Some(&serde_json::json!({ "body": body.as_ref() }))) .await } /// Deletes a comment in an issue. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// octocrab::instance().issues("owner", "repo").delete_comment(101u64.into()).await?; /// # Ok(()) /// # } /// ``` pub async fn delete_comment(&self, comment_id: CommentId) -> Result<()> { let route = format!( "/repos/{owner}/{repo}/issues/comments/{comment_id}", owner = self.owner, repo = self.repo, ); let uri = Uri::builder() .path_and_query(route) .build() .context(HttpSnafu)?; let response = self.crab._delete(uri, None::<&()>).await?; if response.status() == 204 { Ok(()) } else { crate::map_github_error(response).await.map(drop) } } /// Lists comments in the issue. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let comment = octocrab::instance() /// .issues("owner", "repo") /// .list_comments(101u64.into()) /// .since(chrono::Utc::now()) /// .per_page(100) /// .page(2u32) /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn list_comments(&self, issue_number: u64) -> ListCommentsBuilder<'_, '_> { ListCommentsBuilder::new(self, issue_number) } /// Lists comments for issues in the whole repo. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let comment = octocrab::instance() /// .issues("owner", "repo") /// .list_issue_comments() /// .per_page(100) /// .page(2u32) /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn list_issue_comments(&self) -> ListIssueCommentsBuilder<'_, '_> { ListIssueCommentsBuilder::new(self) } } #[derive(serde::Serialize)] pub struct ListCommentsBuilder<'octo, 'r> { #[serde(skip)] handler: &'r IssueHandler<'octo>, #[serde(skip)] issue_number: u64, #[serde(skip_serializing_if = "Option::is_none")] since: Option>, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo, 'r> ListCommentsBuilder<'octo, 'r> { pub(crate) fn new(handler: &'r IssueHandler<'octo>, issue_number: u64) -> Self { Self { handler, issue_number, since: None, per_page: None, page: None, } } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Only comments updated at or after this time are returned. pub fn since(mut self, since: impl Into>) -> Self { self.since = Some(since.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Send the actual request. pub async fn send(self) -> Result> { let route = format!( "/repos/{owner}/{repo}/issues/{issue}/comments", owner = self.handler.owner, repo = self.handler.repo, issue = self.issue_number, ); self.handler.crab.get(route, Some(&self)).await } } #[derive(serde::Serialize)] pub struct ListIssueCommentsBuilder<'octo, 'r> { #[serde(skip)] handler: &'r IssueHandler<'octo>, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, #[serde(skip_serializing_if = "Option::is_none")] sort: Option, #[serde(skip_serializing_if = "Option::is_none")] direction: Option, #[serde(skip_serializing_if = "Option::is_none")] since: Option>, } impl<'octo, 'r> ListIssueCommentsBuilder<'octo, 'r> { pub(crate) fn new(handler: &'r IssueHandler<'octo>) -> Self { Self { handler, per_page: None, page: None, sort: None, direction: None, since: None, } } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Specify sort results by. Can be either `created`, `updated`, pub fn sort(mut self, sort: impl Into) -> Self { self.sort = Some(sort.into()); self } /// The direction of the sort. Can be either ascending or descending. pub fn direction(mut self, direction: impl Into) -> Self { self.direction = Some(direction.into()); self } /// Only show comments updated after the given time. pub fn since(mut self, since: impl Into>) -> Self { self.since = Some(since.into()); self } /// Send the actual request. pub async fn send(self) -> Result> { let route = format!( "/repos/{owner}/{repo}/issues/comments", owner = self.handler.owner, repo = self.handler.repo, ); self.handler.crab.get(route, Some(&self)).await } } #[derive(serde::Serialize)] pub struct ListTimelineEventsBuilder<'octo, 'r> { #[serde(skip)] handler: &'r IssueHandler<'octo>, issue_number: u64, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo, 'r> ListTimelineEventsBuilder<'octo, 'r> { pub(crate) fn new(handler: &'r IssueHandler<'octo>, issue_number: u64) -> Self { Self { handler, issue_number, per_page: None, page: None, } } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Send the actual request. pub async fn send(self) -> Result> { let route = format!( "/repos/{owner}/{repo}/issues/{issue}/timeline", owner = self.handler.owner, repo = self.handler.repo, issue = self.issue_number, ); self.handler.crab.get(route, Some(&self)).await } } // Timeline impl<'octo> IssueHandler<'octo> { /// Lists events in the issue timeline. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let timeline = octocrab::instance() /// .issues("owner", "repo") /// .list_timeline_events(21u64.into()) /// .per_page(100) /// .page(2u32) /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn list_timeline_events(&self, issue_number: u64) -> ListTimelineEventsBuilder<'_, '_> { ListTimelineEventsBuilder::new(self, issue_number) } } impl<'octo> IssueHandler<'octo> { /// Lists reactions for an issue. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let reactions = octocrab::instance() /// .issues("owner", "repo") /// .list_reactions(1) /// .per_page(100) /// .page(2u32) /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn list_reactions(&self, issue_number: u64) -> ListReactionsBuilder<'_, '_> { ListReactionsBuilder::new(self, issue_number) } /// Lists reactions for an issue comment. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let reactions = octocrab::instance() /// .issues("owner", "repo") /// .list_comment_reactions(1) /// .per_page(100) /// .page(2u32) /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn list_comment_reactions( &self, comment_id: impl Into, ) -> ListCommentReactionsBuilder<'_, '_> { ListCommentReactionsBuilder::new(self, comment_id.into()) } } #[derive(serde::Serialize)] pub struct ListReactionsBuilder<'octo, 'r> { #[serde(skip)] handler: &'r IssueHandler<'octo>, issue_number: u64, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo, 'r> ListReactionsBuilder<'octo, 'r> { pub(crate) fn new(handler: &'r IssueHandler<'octo>, issue_number: u64) -> Self { Self { handler, issue_number, per_page: None, page: None, } } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Send the actual request. pub async fn send(self) -> Result> { let route = format!( "/repos/{owner}/{repo}/issues/{issue}/reactions", owner = self.handler.owner, repo = self.handler.repo, issue = self.issue_number, ); self.handler.crab.get(route, Some(&self)).await } } #[derive(serde::Serialize)] pub struct ListCommentReactionsBuilder<'octo, 'r> { #[serde(skip)] handler: &'r IssueHandler<'octo>, comment_id: CommentId, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo, 'r> ListCommentReactionsBuilder<'octo, 'r> { pub(crate) fn new(handler: &'r IssueHandler<'octo>, comment_id: CommentId) -> Self { Self { handler, comment_id, per_page: None, page: None, } } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Send the actual request. pub async fn send(self) -> Result> { let route = format!( "/repos/{owner}/{repo}/issues/comments/{comment}/reactions", owner = self.handler.owner, repo = self.handler.repo, comment = self.comment_id, ); self.handler.crab.get(route, Some(&self)).await } } impl<'octo> IssueHandler<'octo> { /// Creates a reaction for an issue. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// octocrab::instance() /// .issues("owner", "repo") /// .create_reaction(1, octocrab::models::reactions::ReactionContent::PlusOne) /// .await?; /// # Ok(()) /// # } /// ``` pub async fn create_reaction( &self, issue_number: u64, content: models::reactions::ReactionContent, ) -> Result { let route = format!( "/repos/{owner}/{repo}/issues/{issue_number}/reactions", owner = self.owner, repo = self.repo, ); self.crab .post(route, Some(&serde_json::json!({ "content": content }))) .await } /// Creates a reaction for an issue comment. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// octocrab::instance() /// .issues("owner", "repo") /// .create_comment_reaction(1, octocrab::models::reactions::ReactionContent::PlusOne) /// .await?; /// # Ok(()) /// # } /// ``` pub async fn create_comment_reaction( &self, comment_id: impl Into, content: models::reactions::ReactionContent, ) -> Result { let route = format!( "/repos/{owner}/{repo}/issues/comments/{comment_id}/reactions", owner = self.owner, repo = self.repo, comment_id = comment_id.into(), ); self.crab .post(route, Some(&serde_json::json!({ "content": content }))) .await } } impl<'octo> IssueHandler<'octo> { /// Deletes a reaction for an issue. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// octocrab::instance() /// .issues("owner", "repo") /// .delete_reaction(1, 1) /// .await?; /// # Ok(()) /// # } /// ``` pub async fn delete_reaction( &self, issue_number: u64, reaction_id: impl Into, ) -> Result<()> { let route = format!( "/repos/{owner}/{repo}/issues/{issue_number}/reactions/{reaction_id}", owner = self.owner, repo = self.repo, reaction_id = reaction_id.into(), ); self.crab.delete(route, None::<&()>).await } /// Deletes a reaction for an issue comment. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// octocrab::instance() /// .issues("owner", "repo") /// .delete_comment_reaction(1, 1) /// .await?; /// # Ok(()) /// # } /// ``` pub async fn delete_comment_reaction( &self, comment_id: impl Into, reaction_id: impl Into, ) -> Result<()> { let route = format!( "/repos/{owner}/{repo}/issues/comments/{comment_id}/reactions/{reaction_id}", owner = self.owner, repo = self.repo, comment_id = comment_id.into(), reaction_id = reaction_id.into(), ); self.crab.delete(route, None::<&()>).await } } octocrab-0.31.2/src/api/licenses.rs000064400000000000000000000022531046102023000152200ustar 00000000000000//! Metadata about popular open source licenses and information about a //! project's license file. use crate::{models, Octocrab}; /// Handler for GitHub's license API. /// /// Created with [`Octocrab::licenses`]. pub struct LicenseHandler<'octo> { crab: &'octo Octocrab, } impl<'octo> LicenseHandler<'octo> { pub(crate) fn new(crab: &'octo Octocrab) -> Self { Self { crab } } /// List commonly used licenses. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let licenses = octocrab::instance().licenses().list_commonly_used().await?; /// # Ok(()) /// # } /// ``` pub async fn list_commonly_used(&self) -> crate::Result> { self.crab.get("/licenses", None::<&()>).await } /// Get an individual license. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let license = octocrab::instance().licenses().get("mit").await?; /// # Ok(()) /// # } /// ``` pub async fn get(&self, key: impl AsRef) -> crate::Result { self.crab .get(format!("/licenses/{}", key.as_ref()), None::<&()>) .await } } octocrab-0.31.2/src/api/markdown.rs000064400000000000000000000076101046102023000152370ustar 00000000000000//! The markdown API use crate::error::HttpSnafu; use http::request::Builder; use http::{Method, Uri}; use snafu::ResultExt; use crate::Octocrab; /// Handler for GitHub's markdown API. /// /// Created with [`Octocrab::markdown`]. pub struct MarkdownHandler<'octo> { crab: &'octo Octocrab, } impl<'octo> MarkdownHandler<'octo> { pub(crate) fn new(crab: &'octo Octocrab) -> Self { Self { crab } } /// Render an arbitrary Markdown document. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// use octocrab::params; /// /// let markdown = octocrab::instance() /// .markdown() /// .render("Comment referencing issue #404") /// .mode(params::markdown::Mode::Gfm) /// .context("owner/repo") /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn render<'r, 'text>( &'r self, text: &'text (impl AsRef + ?Sized), ) -> RenderMarkdownBuilder<'octo, 'r, 'text> { RenderMarkdownBuilder::new(self, text.as_ref()) } /// Render a Markdown document in raw mode. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// use octocrab::params; /// /// let markdown = octocrab::instance() /// .markdown() /// .render_raw("~~_**Octocrab**_~~") /// .await?; /// # Ok(()) /// # } /// ``` pub async fn render_raw(&self, text: impl Into) -> crate::Result { let uri = Uri::builder() .path_and_query("/markdown/raw") .build() .context(HttpSnafu)?; let mut request = Builder::new().uri(uri).method(Method::POST); request = request.header(http::header::CONTENT_TYPE, "text/x-markdown"); let request = self.crab.build_request(request, Some(&text.into()))?; self.crab .body_to_string(self.crab.execute(request).await?) .await } } #[derive(serde::Serialize)] pub struct RenderMarkdownBuilder<'octo, 'r, 'text> { #[serde(skip)] handler: &'r MarkdownHandler<'octo>, text: &'text str, #[serde(skip_serializing_if = "Option::is_none")] mode: Option, #[serde(skip_serializing_if = "Option::is_none")] context: Option, } impl<'octo, 'r, 'text> RenderMarkdownBuilder<'octo, 'r, 'text> { pub(crate) fn new(handler: &'r MarkdownHandler<'octo>, text: &'text str) -> Self { Self { handler, text, mode: None, context: None, } } /// The repository context to use when creating references in `Mode::Gfm`. /// Omit this parameter when using markdown mode. pub fn context>(mut self, context: impl Into>) -> Self { self.context = context.into().map(A::into); self } /// The rendering mode. pub fn mode(mut self, mode: impl Into>) -> Self { self.mode = mode.into(); self } /// Send the actual request. pub async fn send(self) -> crate::Result { let uri = Uri::builder() .path_and_query("/markdown") .build() .context(HttpSnafu)?; self.handler .crab .body_to_string(self.handler.crab._post(uri, Some(&self)).await?) .await } } #[cfg(test)] mod tests { #[tokio::test] async fn serialize() { let octocrab = crate::instance(); let handler = octocrab.markdown(); let render = handler .render("**Markdown**") .mode(crate::params::markdown::Mode::Gfm) .context("owner/repo"); assert_eq!( serde_json::to_value(render).unwrap(), serde_json::json!({ "text": "**Markdown**", "mode": "gfm", "context": "owner/repo", }) ) } } octocrab-0.31.2/src/api/orgs/events.rs000064400000000000000000000052051046102023000156710ustar 00000000000000//! GitHub Organization Events use crate::{ etag::{EntityTag, Etagged}, models::events, orgs::OrgHandler, FromResponse, Page, }; use http::request::Builder; use http::{header::HeaderMap, Method, StatusCode}; pub struct ListOrgEventsBuilder<'octo, 'handler> { handler: &'handler OrgHandler<'octo>, headers: Headers, params: Params, } struct Headers { etag: Option, } #[derive(serde::Serialize)] struct Params { #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo, 'handler> ListOrgEventsBuilder<'octo, 'handler> { pub(crate) fn new(handler: &'handler OrgHandler<'octo>) -> Self { Self { handler, headers: Headers { etag: None }, params: Params { per_page: None, page: None, }, } } /// Etag for this request. pub fn etag(mut self, etag: Option) -> Self { self.headers.etag = etag; self } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.params.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.params.page = Some(page.into()); self } /// Sends the actual request. pub async fn send(self) -> crate::Result>> { let route = format!("/orgs/{owner}/events", owner = self.handler.owner); let uri = self .handler .crab .parameterized_uri(route, Some(&self.params))?; let mut headers = HeaderMap::new(); if let Some(etag) = self.headers.etag { EntityTag::insert_if_none_match_header(&mut headers, etag)?; } let mut request = Builder::new().uri(uri).method(Method::GET); for (key, value) in headers.iter() { request = request.header(key, value); } let request = self.handler.crab.build_request(request, None::<&()>)?; let response = self.handler.crab.execute(request).await?; let etag = EntityTag::extract_from_response(&response); if response.status() == StatusCode::NOT_MODIFIED { Ok(Etagged { etag, value: None }) } else { >::from_response(crate::map_github_error(response).await?) .await .map(|page| Etagged { etag, value: Some(page), }) } } } octocrab-0.31.2/src/api/orgs/list_members.rs000064400000000000000000000020601046102023000170460ustar 00000000000000use super::*; #[derive(serde::Serialize)] pub struct ListOrgMembersBuilder<'octo, 'r> { #[serde(skip)] handler: &'r OrgHandler<'octo>, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo, 'r> ListOrgMembersBuilder<'octo, 'r> { pub fn new(handler: &'r OrgHandler<'octo>) -> Self { Self { handler, per_page: None, page: None, } } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } pub async fn send(self) -> crate::Result> { let route = format!("/orgs/{org}/members", org = self.handler.owner); self.handler.crab.get(route, Some(&self)).await } } octocrab-0.31.2/src/api/orgs/list_repos.rs000064400000000000000000000060501046102023000165470ustar 00000000000000use super::*; #[derive(serde::Serialize)] pub struct ListReposBuilder<'octo, 'b> { #[serde(skip)] handler: &'b OrgHandler<'octo>, #[serde(skip_serializing_if = "Option::is_none")] r#type: Option, #[serde(skip_serializing_if = "Option::is_none")] sort: Option, #[serde(skip_serializing_if = "Option::is_none")] direction: Option, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo, 'b> ListReposBuilder<'octo, 'b> { pub(crate) fn new(handler: &'b OrgHandler<'octo>) -> Self { Self { handler, r#type: None, sort: None, direction: None, per_page: None, page: None, } } /// Filter repostories by their type pub fn repo_type(mut self, r#type: impl Into>) -> Self { self.r#type = r#type.into(); self } /// What to sort results by. Can be either `created`, `updated`, `pushed`, /// or `full_name`. pub fn sort(mut self, sort: impl Into) -> Self { self.sort = Some(sort.into()); self } /// The direction of the sort. Can be either `Ascending` or `Descending`. /// Default: ascending when sort is `full_name`, otherwise descending. pub fn direction(mut self, direction: impl Into) -> Self { self.direction = Some(direction.into()); self } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Sends the actual request. pub async fn send(self) -> crate::Result> { let route = format!("/orgs/{owner}/repos", owner = self.handler.owner); self.handler.crab.get(route, Some(&self)).await } } #[cfg(test)] mod tests { #[tokio::test] async fn serialize() { let octocrab = crate::Octocrab::default(); let handler = octocrab.pulls("rust-lang", "rust"); let list = handler .list() .state(crate::params::State::Open) .head("master") .base("branch") .sort(crate::params::pulls::Sort::Popularity) .direction(crate::params::Direction::Ascending) .per_page(100) .page(1u8); assert_eq!( serde_json::to_value(list).unwrap(), serde_json::json!({ "state": "open", "head": "master", "base": "branch", "sort": "popularity", "direction": "asc", "per_page": 100, "page": 1, }) ) } } octocrab-0.31.2/src/api/orgs/secrets.rs000064400000000000000000000144271046102023000160430ustar 00000000000000use http::StatusCode; use snafu::GenerateImplicitData; use super::OrgHandler; use crate::models::orgs::secrets::{CreateOrganizationSecret, CreateOrganizationSecretResponse}; /// A client to GitHub's organization API. /// /// Created with [`Octocrab::orgs`]. pub struct OrgSecretsHandler<'octo> { org: &'octo OrgHandler<'octo>, } impl<'octo> OrgSecretsHandler<'octo> { pub(crate) fn new(org: &'octo OrgHandler<'octo>) -> Self { Self { org } } fn owner(&self) -> &String { &self.org.owner } /// Lists all secrets available in an organization without revealing their encrypted values. /// You must authenticate using an access token with the admin:org scope to use this endpoint. /// GitHub Apps must have the secrets organization permission to use this endpoint. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); /// let org = octocrab.orgs("owner"); /// let secrets = org.secrets(); /// let all_secrets = secrets.get_secrets().await?; /// # Ok(()) /// # } pub async fn get_secrets( &self, ) -> crate::Result { let route = format!("/orgs/{org}/actions/secrets", org = self.owner()); self.org.crab.get(route, None::<&()>).await } // Gets your public key, which you need to encrypt secrets. You need to encrypt a secret before you can create or update secrets. // You must authenticate using an access token with the admin:org scope to use this endpoint. // GitHub Apps must have the secrets organization permission to use this endpoint. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); /// let org = octocrab.orgs("owner"); /// let secrets = org.secrets(); /// let public_key = secrets.get_public_key().await?; /// # Ok(()) /// # } pub async fn get_public_key(&self) -> crate::Result { let route = format!("/orgs/{org}/actions/secrets/public-key", org = self.owner()); self.org.crab.get(route, None::<&()>).await } /// Gets a specific secret from the organization without revealing its encrypted values. /// You must authenticate using an access token with the admin:org scope to use this endpoint. /// GitHub Apps must have the secrets organization permission to use this endpoint. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); /// let org = octocrab.orgs("owner"); /// let secrets = org.secrets(); /// let secret_info = secrets.get_secret("TOKEN").await?; /// # Ok(()) /// # } pub async fn get_secret( &self, secret_name: impl AsRef, ) -> crate::Result { let route = format!( "/orgs/{org}/actions/secrets/{secret_name}", org = self.owner(), secret_name = secret_name.as_ref() ); self.org.crab.get(route, None::<&()>).await } /// Creates or updates an organization secret with an encrypted value. /// Encrypt your secret using [`crypto_box`](https://crates.io/crates/crypto_box). /// You must authenticate using an access token with the admin:org scope to use this endpoint. /// GitHub Apps must have the secrets organization permission to use this endpoint /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); /// use octocrab::models::orgs::secrets::{ /// CreateOrganizationSecret, CreateOrganizationSecretResponse, /// Visibility /// }; /// /// let org = octocrab.orgs("owner"); /// let secrets = org.secrets(); /// let result = secrets.create_or_update_secret("GH_TOKEN", &CreateOrganizationSecret{ /// key_id: "123456", /// encrypted_value: "some-b64-encrypted-string", /// visibility: Visibility::Selected, /// selected_repository_ids: None, /// }).await?; /// /// match result { /// CreateOrganizationSecretResponse::Created => println!("Created secret!"), /// CreateOrganizationSecretResponse::Updated => println!("Updated secret!"), /// } /// # Ok(()) /// # } pub async fn create_or_update_secret( &self, secret_name: impl AsRef, secret: &CreateOrganizationSecret<'_>, ) -> crate::Result { let route = format!( "/orgs/{org}/actions/secrets/{secret_name}", org = self.owner(), secret_name = secret_name.as_ref() ); let resp = { let resp = self.org.crab._put(route, Some(secret)).await?; crate::map_github_error(resp).await? }; match resp.status() { StatusCode::CREATED => Ok(CreateOrganizationSecretResponse::Created), StatusCode::NO_CONTENT => Ok(CreateOrganizationSecretResponse::Updated), status_code => Err(crate::Error::Other { source: format!( "Unexpected status code from request: {}", status_code.as_str() ) .into(), backtrace: snafu::Backtrace::generate(), }), } } /// Deletes an organization secret. /// You must authenticate using an access token with the admin:org scope to use this endpoint. /// GitHub Apps must have the secrets organization permission to use this endpoint /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); /// let org = octocrab.orgs("owner"); /// let secrets = org.secrets(); /// /// secrets.delete_secret("GH_TOKEN").await?; /// /// # Ok(()) /// # } pub async fn delete_secret(&self, secret_name: impl AsRef) -> crate::Result<()> { let route = format!( "/orgs/{org}/actions/secrets/{secret_name}", org = self.owner(), secret_name = secret_name.as_ref() ); let resp = self.org.crab._delete(route, None::<&()>).await?; crate::map_github_error(resp).await?; Ok(()) } } octocrab-0.31.2/src/api/orgs.rs000064400000000000000000000167361046102023000144000ustar 00000000000000//! The Organization API. mod events; mod list_members; mod list_repos; mod secrets; use crate::error::HttpSnafu; use crate::Octocrab; use http::Uri; use snafu::ResultExt; pub use self::events::ListOrgEventsBuilder; pub use self::list_members::ListOrgMembersBuilder; pub use self::list_repos::ListReposBuilder; pub use self::secrets::OrgSecretsHandler; /// A client to GitHub's organization API. /// /// Created with [`Octocrab::orgs`]. pub struct OrgHandler<'octo> { crab: &'octo Octocrab, owner: String, } impl<'octo> OrgHandler<'octo> { pub(crate) fn new(crab: &'octo Octocrab, owner: String) -> Self { Self { crab, owner } } /// Add or update organization membership /// /// **Note** /// - Only authenticated organization owners can add a member to the /// organization or update the member's role. /// - If the authenticated user is adding a member to the organization, the /// invited user will receive an email inviting them to the organization. /// The user's membership status will be pending until they accept /// the invitation. /// - Authenticated users can update a user's membership by passing the role /// parameter. If the authenticated user changes a member's role to admin, /// the affected user will receive an email notifying them that they've /// been made an organization owner. If the authenticated user changes an /// owner's role to member, no email will be sent. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); /// let invitation = octocrab.orgs("owner").add_or_update_membership("ferris", None).await?; /// # Ok(()) /// # } /// ``` pub async fn add_or_update_membership( &self, username: impl AsRef, role: Option, ) -> crate::Result { let route = format!( "/orgs/{org}/memberships/{username}", org = self.owner, username = username.as_ref(), ); let body = role.map(|role| serde_json::json!({ "role": role })); self.crab.post(route, body.as_ref()).await } /// Check if a user is, publicly or privately, a member of the organization. /// /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); /// assert!(octocrab.orgs("owner").check_membership("ferris").await?); /// # Ok(()) /// # } /// ``` pub async fn check_membership(&self, username: impl AsRef) -> crate::Result { let route = format!( "/orgs/{org}/members/{username}", org = self.owner, username = username.as_ref(), ); let uri = Uri::builder() .path_and_query(route) .build() .context(HttpSnafu)?; let response = self.crab._get(uri).await?; let status = response.status(); Ok(status == 204 || status == 301) } /// Get an organization /// /// To see many of the organization response values, you need to be an /// authenticated organization owner with the `admin:org` scope. When the /// value of `two_factor_requirement_enabled` is true, the organization /// requires all members, billing managers, and outside collaborators to /// enable two-factor authentication. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); /// let org = octocrab.orgs("owner").get().await?; /// # Ok(()) /// # } /// ``` pub async fn get(&self) -> crate::Result { let route = format!("/orgs/{org}", org = self.owner); self.crab.get(route, None::<&()>).await } /// List repos for the specified organization. /// /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// use octocrab::params; /// /// // Get the least active repos belonging to `owner`. /// let page = octocrab::instance() /// .orgs("owner") /// .list_repos() /// // Optional Parameters /// .repo_type(params::repos::Type::Sources) /// .sort(params::repos::Sort::Pushed) /// .direction(params::Direction::Descending) /// .per_page(25) /// .page(5u32) /// // Send the request. /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn list_repos(&self) -> list_repos::ListReposBuilder { list_repos::ListReposBuilder::new(self) } /// List events on this organization. /// /// Takes an optional etag which allows for efficient polling. Here is a quick example to poll a /// organization's events. /// ```no_run /// # use std::convert::TryFrom; /// # use octocrab::{models::events::Event, etag::{Etagged,EntityTag}, Page}; /// # async fn run() -> octocrab::Result<()> { /// let mut etag = None; /// loop { /// let response: Etagged> = octocrab::instance() /// .orgs("owner") /// .events() /// .etag(etag) /// .send() /// .await?; /// if let Some(page) = response.value { /// // do something with the page ... /// } else { /// println!("No new data received, trying again soon"); /// } /// etag = response.etag; /// // add a delay before the next iteration /// } /// # Ok(()) /// # } /// ``` pub fn events(&self) -> events::ListOrgEventsBuilder<'_, '_> { events::ListOrgEventsBuilder::new(self) } /// Creates a new webhook for the specified organization. /// /// # Notes /// Only authorized users or apps can modify organization webhooks. /// /// # Examples /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); /// use octocrab::models::hooks::{Hook, Config as HookConfig, ContentType as HookContentType}; /// /// let config = HookConfig { /// url: "https://example.com".to_string(), /// content_type: Some(HookContentType::Json), /// insecure_ssl: None, /// secret: None /// }; /// /// let hook = Hook { /// name: "web".to_string(), /// config, /// ..Hook::default() /// }; /// /// let hook = octocrab.orgs("owner").create_hook(hook).await?; /// # Ok(()) /// # } /// ``` pub async fn create_hook( &self, hook: crate::models::hooks::Hook, ) -> crate::Result { let route = format!("/orgs/{org}/hooks", org = self.owner); let res = self.crab.post(route, Some(&hook)).await?; Ok(res) } /// Lists members of the specified organization. /// /// # Notes /// Only authorized users who belong to the organization can list its members. /// /// # Examples /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let org_members = octocrab::instance().orgs("org").list_members().send().await?; /// # Ok(()) /// # } /// ``` pub fn list_members(&self) -> list_members::ListOrgMembersBuilder { list_members::ListOrgMembersBuilder::new(self) } /// Handle secrets on the organizaton /// ```no_run /// ``` pub fn secrets(&self) -> secrets::OrgSecretsHandler<'_> { secrets::OrgSecretsHandler::new(self) } } octocrab-0.31.2/src/api/projects/projects.rs000064400000000000000000000271571046102023000171070ustar 00000000000000//! A set of helper structs and implementations to manage projects use std::marker::PhantomData; use super::*; /// Helper builder struct to get a project by its id. /// /// Used by [`Octocrab::projects`]. #[derive(serde::Serialize)] pub struct GetProjectBuilder<'octo, 'r> { #[serde(skip)] handler: &'r ProjectHandler<'octo>, project_id: u32, } impl<'octo, 'r> GetProjectBuilder<'octo, 'r> { pub fn new(handler: &'r ProjectHandler<'octo>, project_id: u32) -> Self { Self { handler, project_id, } } pub async fn send(self) -> crate::Result { let route = format!("/projects/{project_id}", project_id = self.project_id); self.handler.crab.get(route, None::<&()>).await } } /// Helper builder struct to update a project by its id and body. /// /// Used by [`Octocrab::projects`]. #[derive(serde::Serialize)] pub struct UpdateProjectBuilder<'octo, 'r, B> where B: Serialize + ?Sized, { #[serde(skip)] handler: &'r ProjectHandler<'octo>, project_id: u32, #[serde(skip_serializing_if = "Option::is_none")] body: Option<&'r B>, } impl<'octo, 'r, B> UpdateProjectBuilder<'octo, 'r, B> where B: Serialize + ?Sized, { pub fn new(handler: &'r ProjectHandler<'octo>, project_id: u32) -> Self { Self { handler, project_id, body: None, } } /// Set the body of the project. /// /// The input parameter `body` can specify the following keys: /// - `name` (string) - name of the project /// - `body` (string or null) - project description /// - `state` (string) - `open` or `closed` /// - `organization_permission` (string) - `read`, `write`, `admin`, `none`) /// - `private` (boolean) pub fn body(mut self, body: &'r B) -> Self { self.body = Some(body); self } pub async fn send(self) -> crate::Result { let route = format!("/projects/{project_id}", project_id = self.project_id); self.handler.crab.patch(route, Some(&self)).await } } /// Helper builder struct to delete a project by its id. /// /// Used by [`Octocrab::projects`]. #[derive(serde::Serialize)] pub struct DeleteProjectBuilder<'octo, 'r> { #[serde(skip)] handler: &'r ProjectHandler<'octo>, project_id: u32, } impl<'octo, 'r> DeleteProjectBuilder<'octo, 'r> { pub fn new(handler: &'r ProjectHandler<'octo>, project_id: u32) -> Self { Self { handler, project_id, } } pub async fn send(self) -> crate::Result { let route = format!("/projects/{project_id}", project_id = self.project_id); self.handler.crab.delete(route, None::<&()>).await } } /// Helper builder struct to create a user project given its name. /// /// Used by [`Octocrab::projects`]. #[derive(serde::Serialize)] pub struct CreateUserProjectBuilder<'octo, 'r> { #[serde(skip)] handler: &'r ProjectHandler<'octo>, #[serde(skip_serializing_if = "Option::is_none")] body: Option, name: String, } impl<'octo, 'r> CreateUserProjectBuilder<'octo, 'r> { pub fn new(handler: &'r ProjectHandler<'octo>, name: String) -> Self { Self { handler, name, body: None, } } // The description of the project pub fn body(mut self, body: impl Into) -> Self { self.body = Some(body.into()); self } pub async fn send(self) -> crate::Result { let route = "/user/projects"; self.handler.crab.post(route, Some(&self.body)).await } } /// Helper builder struct to list user projects given the username of the user. /// /// Used by [`Octocrab::projects`]. #[derive(serde::Serialize)] pub struct ListUserProjectsBuilder<'octo, 'r> { #[serde(skip)] handler: &'r ProjectHandler<'octo>, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, username: String, } impl<'octo, 'r> ListUserProjectsBuilder<'octo, 'r> { pub fn new(handler: &'r ProjectHandler<'octo>, username: String) -> Self { Self { handler: handler, username: username, per_page: None, page: None, } } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } pub async fn send(self) -> crate::Result> { let route = format!("/users/{username}/projects", username = self.username); self.handler.crab.get(route, None::<&()>).await } } /// Helper builder struct to get a paged list of an organization's projects. /// /// Used by [`Octocrab::projects`]. #[derive(serde::Serialize)] pub struct ListOrgProjectsBuilder<'octo, 'r> { #[serde(skip)] handler: &'r ProjectHandler<'octo>, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, #[serde(skip_serializing_if = "Option::is_none")] state: Option, org: String, } impl<'octo, 'r> ListOrgProjectsBuilder<'octo, 'r> { pub fn new(handler: &'r ProjectHandler<'octo>, org: String) -> Self { Self { handler, per_page: None, page: None, state: None, org: org, } } // Indicates the state of the projects to return. // // * `state` - state of the project. Default is `open` // can be one of `open`, `closed`, `all` pub fn state(mut self, state: impl Into) -> Self { self.state = Some(state.into()); self } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } pub async fn send(self) -> crate::Result> { let route = format!("/orgs/{org}/projects", org = self.org); self.handler.crab.get(route, Some(&self)).await } } /// Helper builder struct to create an organization project. /// /// Used by [`Octocrab::projects`]. #[derive(serde::Serialize)] pub struct CreateOrgProjectsBuilder<'octo, 'r> { #[serde(skip)] handler: &'r ProjectHandler<'octo>, #[serde(skip_serializing_if = "Option::is_none")] body: Option, name: String, org: String, } impl<'octo, 'r> CreateOrgProjectsBuilder<'octo, 'r> { pub fn new(handler: &'r ProjectHandler<'octo>, org: String, name: String) -> Self { Self { handler, body: None, name: name, org: org, } } /// Description of the project. pub fn body(mut self, description: impl Into) -> Self { self.body = Some(description.into()); self } pub async fn send(self) -> crate::Result { let route = format!("/orgs/{org}/projects", org = self.org); self.handler.crab.post(route, Some(&self)).await } } /// Helper builder struct to get a paged list of repository projects /// /// Used by [`Octocrab::projects`]. #[derive(serde::Serialize)] pub struct ListRepositoryProjectsBuilder<'octo, 'r> { #[serde(skip)] handler: &'r ProjectHandler<'octo>, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, owner: String, repo: String, } impl<'octo, 'r> ListRepositoryProjectsBuilder<'octo, 'r> { pub fn new(handler: &'r ProjectHandler<'octo>, owner: String, repo: String) -> Self { Self { handler, per_page: None, page: None, owner: owner, repo: repo, } } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Sends the actual request. pub async fn send(self) -> crate::Result> { let route = format!( "/repos/{owner}/{repo}/projects", owner = self.owner, repo = self.repo ); self.handler.crab.get(route, Some(&self)).await } } pub struct Named; pub struct NotNamed; /// Helper builder struct to get create a repository project /// /// Used by [`Octocrab::projects`]. #[derive(serde::Serialize)] pub struct CreateRepositoryProjectsBuilder<'octo, 'r, S> { #[serde(skip)] handler: &'r ProjectHandler<'octo>, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, #[serde(skip_serializing_if = "Option::is_none")] body: Option, owner: String, repo: String, project_name: Option, _named: std::marker::PhantomData, } impl<'octo, 'r> CreateRepositoryProjectsBuilder<'octo, 'r, NotNamed> { pub fn instantiate(handler: &'r ProjectHandler<'octo>, owner: String, repo: String) -> Self { Self { handler, per_page: None, page: None, body: None, owner: owner, repo: repo, project_name: None, _named: PhantomData::default(), } } pub fn project_name( self, name: impl Into, ) -> CreateRepositoryProjectsBuilder<'octo, 'r, Named> { CreateRepositoryProjectsBuilder { handler: self.handler, per_page: self.per_page, page: self.page, body: self.body, owner: self.owner, repo: self.repo, project_name: Some(name.into()), _named: PhantomData::default(), } } } impl<'octo, 'r> CreateRepositoryProjectsBuilder<'octo, 'r, Named> { pub fn new(repo_builder: CreateRepositoryProjectsBuilder<'octo, 'r, NotNamed>) -> Self { Self { handler: repo_builder.handler, per_page: None, page: None, body: None, owner: repo_builder.owner, repo: repo_builder.repo, project_name: repo_builder.project_name, _named: PhantomData::default(), } } /// Description of the project. pub fn body(mut self, description: impl Into) -> Self { self.body = Some(description.into()); self } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Sends the actual request. pub async fn send(self) -> crate::Result { let route = format!( "/repos/{owner}/{repo}/projects", owner = self.owner, repo = self.repo ); self.handler.crab.post(route, Some(&self)).await } } octocrab-0.31.2/src/api/projects.rs000064400000000000000000000156711046102023000152540ustar 00000000000000//! The Projects API. //! //! # Notes //! Users need an account with sufficient privileges to interact with projects. mod projects; use self::projects::{ CreateOrgProjectsBuilder, CreateRepositoryProjectsBuilder, CreateUserProjectBuilder, DeleteProjectBuilder, GetProjectBuilder, ListOrgProjectsBuilder, ListRepositoryProjectsBuilder, ListUserProjectsBuilder, NotNamed, UpdateProjectBuilder, }; use crate::Octocrab; use serde::Serialize; /// A struct to access GitHub's projects API. /// /// Created with [`Octocrab::projects`]. pub struct ProjectHandler<'octo> { crab: &'octo Octocrab, } impl<'octo> ProjectHandler<'octo> { pub(crate) fn new(crab: &'octo Octocrab) -> Self { Self { crab } } /// Get a project by its id. /// /// # Arguments /// /// * `project_id` - id of the project to fetch /// /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let project_id: u32 = 1002604; /// let project = octocrab::instance() /// .projects() /// .get_project(project_id) /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn get_project(&self, project_id: impl Into) -> GetProjectBuilder<'_, '_> { GetProjectBuilder::new(self, project_id.into()) } /// Updates a project given its project id. /// /// # Arguments /// /// * `project_id` - id of the project to update /// /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let body = serde_json::json!({ "name": "Week One Sprint", "state": "open" }); /// let project_id: u32 = 1002604; /// let project = octocrab::instance() /// .projects() /// .update_project(project_id) /// .body(&body) /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn update_project(&self, project_id: impl Into) -> UpdateProjectBuilder<'_, '_, B> where B: Serialize + ?Sized, { UpdateProjectBuilder::new(self, project_id.into()) } /// Deletes a project board. /// /// # Arguments /// /// * `project_id` - id of the project to delete /// /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let project_id: u32 = 1002604; /// let project = octocrab::instance() /// .projects() /// .delete_project(project_id) /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn delete_project(&self, project_id: impl Into) -> DeleteProjectBuilder { DeleteProjectBuilder::new(self, project_id.into()) } /// Creates a user project board given its name. /// /// # Arguments /// /// * `username` - account username /// /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let username = "octocat"; /// let description = "Project Overview"; /// let project = octocrab::instance() /// .projects() /// .create_user_project(username) /// .body(description) /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn create_user_project(&self, username: impl Into) -> CreateUserProjectBuilder { CreateUserProjectBuilder::new(self, username.into()) } /// List a user's projects the username of the user /// /// # Arguments /// /// * `username` - account unsername /// /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let username = "octocat"; /// let project = octocrab::instance() /// .projects() /// .list_user_projects(username) /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn list_user_projects(&self, username: impl Into) -> ListUserProjectsBuilder { ListUserProjectsBuilder::new(self, username.into()) } /// List the projects of an organization. /// /// # Notes /// Only users with sufficient privileges can list an organization's projects. /// /// # Arguments /// /// * `org` - name of the organization /// /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let org = "octocrab"; /// let projects = octocrab::instance() /// .projects() /// .list_organization_projects(org) /// .state("all") /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn list_organization_projects( &self, org: impl Into, ) -> ListOrgProjectsBuilder<'_, '_> { ListOrgProjectsBuilder::new(self, org.into()) } /// Create an organization project board. /// /// # Arguments /// /// * `org` - organization name. /// * `name` - name of the project. /// /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let org = "octocrab"; /// let name = "Organization Roadmap"; /// let project = octocrab::instance() /// .projects() /// .create_organization_project(org, name) /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn create_organization_project( &self, org: impl Into, name: impl Into, ) -> CreateOrgProjectsBuilder<'_, '_> { CreateOrgProjectsBuilder::new(self, org.into(), name.into()) } /// Creates a repository project board. /// /// # Arguments /// /// * `owner` - repository owner. /// * `repo` - repository name. /// /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let owner = "octocat"; /// let repo = "octocrab"; /// let name = "My Project"; /// let description = "Project Overview"; /// let tags = octocrab::instance() /// .projects() /// .create_repository_project("owner", "repo") /// .project_name(name) /// .body(description) /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn create_repository_project( &self, owner: impl Into, repo: impl Into, ) -> CreateRepositoryProjectsBuilder<'_, '_, NotNamed> { CreateRepositoryProjectsBuilder::instantiate(self, owner.into(), repo.into()) } /// Lists the projects in a repository. /// /// # Arguments /// /// * `owner` - repository owner. /// * `repo` - repository name. /// /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let owner = "octocat"; /// let repo = "octocrab"; /// let tags = octocrab::instance() /// .projects() /// .list_repository_projects("owner", "repo") /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn list_repository_projects( &self, owner: impl Into, repo: impl Into, ) -> ListRepositoryProjectsBuilder<'_, '_> { ListRepositoryProjectsBuilder::new(self, owner.into(), repo.into()) } } octocrab-0.31.2/src/api/pulls/comment.rs000064400000000000000000000070701046102023000162160ustar 00000000000000use super::*; /// A builder pattern struct for listing comments. /// /// created by [`PullRequestHandler::list_comments`] /// /// [`PullRequestHandler::list_comments`]: ./struct.PullRequestHandler.html#method.list_comments #[derive(serde::Serialize)] pub struct ListCommentsBuilder<'octo, 'b> { #[serde(skip)] handler: &'b PullRequestHandler<'octo>, #[serde(skip)] pr: Option, #[serde(skip_serializing_if = "Option::is_none")] sort: Option, #[serde(skip_serializing_if = "Option::is_none")] direction: Option, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, #[serde(skip_serializing_if = "Option::is_none")] since: Option>, } impl<'octo, 'b> ListCommentsBuilder<'octo, 'b> { pub(crate) fn new(handler: &'b PullRequestHandler<'octo>, pr: Option) -> Self { Self { handler, pr, sort: None, direction: None, per_page: None, page: None, since: None, } } /// What to sort results by. Can be either `created` or `updated`, pub fn sort(mut self, sort: impl Into) -> Self { self.sort = Some(sort.into()); self } /// The direction of the sort. Can be either ascending or descending. /// Default: descending when sort is `created` or sort is not specified, /// otherwise ascending sort. pub fn direction(mut self, direction: impl Into) -> Self { self.direction = Some(direction.into()); self } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Only show notifications updated after the given time. pub fn since(mut self, since: impl Into>) -> Self { self.since = Some(since.into()); self } /// Sends the actual request. pub async fn send(self) -> crate::Result> { let route = format!( "/repos/{owner}/{repo}/pulls/{pr}comments", owner = self.handler.owner, repo = self.handler.repo, pr = if let Some(pr) = self.pr { pr.to_string() + "/" } else { "".into() }, ); self.handler.http_get(route, Some(&self)).await } } #[cfg(test)] mod tests { #[tokio::test] async fn serialize() { let octocrab = crate::Octocrab::default(); let handler = octocrab.pulls("rust-lang", "rust"); let yesterday = chrono::Utc::now() - chrono::Duration::days(1); let list = handler .list_comments(Some(1)) .sort(crate::params::pulls::comments::Sort::Updated) .direction(crate::params::Direction::Ascending) .since(yesterday) .per_page(100) .page(1u8); assert_eq!( serde_json::to_value(list).unwrap(), serde_json::json!({ "sort": "updated", "direction": "asc", "per_page": 100, "page": 1, "since": yesterday }) ) } } octocrab-0.31.2/src/api/pulls/create.rs000064400000000000000000000053051046102023000160160ustar 00000000000000/// A builder pattern struct for constructing an Octocrab request to create a /// pull request. #[derive(serde::Serialize)] pub struct CreatePullRequestBuilder<'octo, 'b> { #[serde(skip)] handler: &'b super::PullRequestHandler<'octo>, title: String, head: String, base: String, #[serde(skip_serializing_if = "Option::is_none")] body: Option, #[serde(skip_serializing_if = "Option::is_none")] draft: Option, #[serde(skip_serializing_if = "Option::is_none")] maintainer_can_modify: Option, } impl<'octo, 'b> CreatePullRequestBuilder<'octo, 'b> { pub(crate) fn new( handler: &'b super::PullRequestHandler<'octo>, title: impl Into, head: impl Into, base: impl Into, ) -> Self { Self { handler, title: title.into(), head: head.into(), base: base.into(), body: None, draft: None, maintainer_can_modify: None, } } /// The contents of the pull request. pub fn body>(mut self, body: impl Into>) -> Self { self.body = body.into().map(A::into); self } /// Indicates whether the pull request is a draft. pub fn draft(mut self, draft: impl Into>) -> Self { self.draft = draft.into(); self } /// Indicates whether `maintainers` can modify the pull request. pub fn maintainer_can_modify(mut self, maintainer_can_modify: impl Into>) -> Self { self.maintainer_can_modify = maintainer_can_modify.into(); self } /// Sends the request to create the pull request. pub async fn send(self) -> crate::Result { let route = format!( "/repos/{owner}/{repo}/pulls", owner = self.handler.owner, repo = self.handler.repo ); self.handler.http_post(route, Some(&self)).await } } #[cfg(test)] mod tests { #[tokio::test] async fn serialize() { let octocrab = crate::Octocrab::default(); let handler = octocrab.pulls("rust-lang", "rust"); let list = handler .create("test-pr", "master", "branch") .body(String::from("testing...")) .draft(true) .maintainer_can_modify(true); assert_eq!( serde_json::to_value(list).unwrap(), serde_json::json!({ "title": "test-pr", "head": "master", "base": "branch", "body": "testing...", "draft": true, "maintainer_can_modify": true, }) ) } } octocrab-0.31.2/src/api/pulls/list.rs000064400000000000000000000101711046102023000155230ustar 00000000000000use super::*; /// A builder pattern struct for listing pull requests. /// /// created by [`PullRequestHandler::list`] /// /// [`PullRequestHandler::list`]: ./struct.PullRequestHandler.html#method.list #[derive(serde::Serialize)] pub struct ListPullRequestsBuilder<'octo, 'b> { #[serde(skip)] handler: &'b PullRequestHandler<'octo>, #[serde(skip_serializing_if = "Option::is_none")] state: Option, #[serde(skip_serializing_if = "Option::is_none")] head: Option, #[serde(skip_serializing_if = "Option::is_none")] base: Option, #[serde(skip_serializing_if = "Option::is_none")] sort: Option, #[serde(skip_serializing_if = "Option::is_none")] direction: Option, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo, 'b> ListPullRequestsBuilder<'octo, 'b> { pub(crate) fn new(handler: &'b PullRequestHandler<'octo>) -> Self { Self { handler, state: None, head: None, base: None, sort: None, direction: None, per_page: None, page: None, } } /// Filter pull requests by `state`. pub fn state(mut self, state: crate::params::State) -> Self { self.state = Some(state); self } /// Filter pull requests by head user or head organization and branch name /// in the format of `user:ref-name` or `organization:ref-name`. For /// example: `github:new-script-format` or `octocrab:test-branch`. pub fn head(mut self, head: impl Into) -> Self { self.head = Some(head.into()); self } /// Filter pulls by base branch name. Example: `gh-pages`. pub fn base(mut self, base: impl Into) -> Self { self.base = Some(base.into()); self } /// What to sort results by. Can be either `created`, `updated`, /// `popularity` (comment count) or `long-running` (age, filtering by pulls /// updated in the last month). pub fn sort(mut self, sort: impl Into) -> Self { self.sort = Some(sort.into()); self } /// The direction of the sort. Can be either ascending or descending. /// Default: descending when sort is `created` or sort is not specified, /// otherwise ascending sort. pub fn direction(mut self, direction: impl Into) -> Self { self.direction = Some(direction.into()); self } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Sends the actual request. pub async fn send(self) -> crate::Result> { let route = format!( "/repos/{owner}/{repo}/pulls", owner = self.handler.owner, repo = self.handler.repo ); self.handler.http_get(route, Some(&self)).await } } #[cfg(test)] mod tests { #[tokio::test] async fn serialize() { let octocrab = crate::Octocrab::default(); let handler = octocrab.pulls("rust-lang", "rust"); let list = handler .list() .state(crate::params::State::Open) .head("master") .base("branch") .sort(crate::params::pulls::Sort::Popularity) .direction(crate::params::Direction::Ascending) .per_page(100) .page(1u8); assert_eq!( serde_json::to_value(list).unwrap(), serde_json::json!({ "state": "open", "head": "master", "base": "branch", "sort": "popularity", "direction": "asc", "per_page": 100, "page": 1, }) ) } } octocrab-0.31.2/src/api/pulls/merge.rs000064400000000000000000000057021046102023000156530ustar 00000000000000use super::*; /// A builder pattern struct for merging pull requests. /// /// created by [`PullRequestHandler::merge`] /// /// [`PullRequestHandler::merge`]: ./struct.PullRequestHandler.html#method.merge #[derive(serde::Serialize)] pub struct MergePullRequestsBuilder<'octo, 'b> { #[serde(skip)] handler: &'b PullRequestHandler<'octo>, #[serde(skip)] pr_number: u64, #[serde(skip_serializing_if = "Option::is_none")] commit_title: Option, #[serde(skip_serializing_if = "Option::is_none")] commit_message: Option, #[serde(skip_serializing_if = "Option::is_none")] sha: Option, #[serde(skip_serializing_if = "Option::is_none")] merge_method: Option, } impl<'octo, 'b> MergePullRequestsBuilder<'octo, 'b> { pub(crate) fn new(handler: &'b PullRequestHandler<'octo>, pr_number: u64) -> Self { Self { handler, pr_number, commit_title: None, commit_message: None, sha: None, merge_method: None, } } /// Title for the automatic commit message. pub fn title(mut self, title: impl Into) -> Self { self.commit_title = Some(title.into()); self } /// Extra detail to append to automatic commit message. pub fn message(mut self, msg: impl Into) -> Self { self.commit_message = Some(msg.into()); self } /// SHA that pull request head must match to allow merge. pub fn sha(mut self, sha: impl Into) -> Self { self.sha = Some(sha.into()); self } /// Merge method to use. Default is `Merge`. pub fn method(mut self, method: impl Into) -> Self { self.merge_method = Some(method.into()); self } /// Sends the actual request. pub async fn send(self) -> crate::Result { let route = format!( "/repos/{owner}/{repo}/pulls/{pull_number}/merge", owner = self.handler.owner, repo = self.handler.repo, pull_number = self.pr_number, ); self.handler.http_put(route, Some(&self)).await } } #[cfg(test)] mod tests { #[tokio::test] async fn serialize() { let octocrab = crate::Octocrab::default(); let handler = octocrab.pulls("rust-lang", "rust"); let merge = handler .merge(80818) .title("just testing!") .message("promise!") .sha("luckily this won't deserialize ;)") .method(crate::params::pulls::MergeMethod::Squash); assert_eq!( serde_json::to_value(merge).unwrap(), serde_json::json!({ "commit_title": "just testing!", "commit_message": "promise!", "sha": "luckily this won't deserialize ;)", "merge_method": "squash", }) ) } } octocrab-0.31.2/src/api/pulls/update.rs000064400000000000000000000063711046102023000160410ustar 00000000000000/// A builder pattern struct for constructing an Octocrab request to update a /// pull request. #[derive(serde::Serialize)] pub struct UpdatePullRequestBuilder<'octo, 'b> { #[serde(skip)] handler: &'b super::PullRequestHandler<'octo>, pull_number: u64, #[serde(skip_serializing_if = "Option::is_none")] title: Option, #[serde(skip_serializing_if = "Option::is_none")] base: Option, #[serde(skip_serializing_if = "Option::is_none")] state: Option, #[serde(skip_serializing_if = "Option::is_none")] body: Option, #[serde(skip_serializing_if = "Option::is_none")] maintainer_can_modify: Option, } impl<'octo, 'b> UpdatePullRequestBuilder<'octo, 'b> { pub(crate) fn new( handler: &'b super::PullRequestHandler<'octo>, pull_number: impl Into, ) -> Self { Self { handler, pull_number: pull_number.into(), title: None, base: None, state: None, body: None, maintainer_can_modify: None, } } /// The contents of the pull request. pub fn body>(mut self, body: impl Into>) -> Self { self.body = body.into().map(A::into); self } /// The contents of the pull request. pub fn title>(mut self, title: impl Into>) -> Self { self.title = title.into().map(A::into); self } /// The contents of the pull request. pub fn base>(mut self, base: impl Into>) -> Self { self.base = base.into().map(A::into); self } /// The contents of the pull request. pub fn state>( mut self, state: impl Into>, ) -> Self { self.state = state.into().map(A::into); self } /// Indicates whether `maintainers` can modify the pull request. pub fn maintainer_can_modify(mut self, maintainer_can_modify: impl Into>) -> Self { self.maintainer_can_modify = maintainer_can_modify.into(); self } /// Sends the request to update the pull request. pub async fn send(self) -> crate::Result { let route = format!( "/repos/{owner}/{repo}/pulls/{pr}", owner = self.handler.owner, repo = self.handler.repo, pr = self.pull_number, ); self.handler.http_patch(route, Some(&self)).await } } #[cfg(test)] mod tests { #[tokio::test] async fn serialize() { let octocrab = crate::Octocrab::default(); let handler = octocrab.pulls("rust-lang", "rust"); let list = handler .update(1) .title("title") .body(String::from("testing...")) .state(crate::params::pulls::State::Open) .maintainer_can_modify(true); assert_eq!( serde_json::to_value(list).unwrap(), serde_json::json!({ "pull_number": 1, "title": "title", "body": "testing...", "state": "open", "maintainer_can_modify": true, }) ) } } octocrab-0.31.2/src/api/pulls.rs000064400000000000000000000420151046102023000145520ustar 00000000000000//! The pull request API. mod comment; mod create; mod list; mod merge; mod update; use http::request::Builder; use http::{Method, Uri}; use snafu::ResultExt; use crate::error::HttpSnafu; use crate::{Octocrab, Page}; pub use self::{ create::CreatePullRequestBuilder, list::ListPullRequestsBuilder, update::UpdatePullRequestBuilder, }; /// A client to GitHub's pull request API. /// /// Created with [`Octocrab::pulls`]. pub struct PullRequestHandler<'octo> { crab: &'octo Octocrab, owner: String, repo: String, media_type: Option, } impl<'octo> PullRequestHandler<'octo> { pub(crate) fn new(crab: &'octo Octocrab, owner: String, repo: String) -> Self { Self { crab, owner, repo, media_type: None, } } /// Set the media type for this request. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let pr = octocrab::instance() /// .pulls("owner", "repo") /// .media_type(octocrab::params::pulls::MediaType::Full) /// .get(404) /// .await?; /// # Ok(()) /// # } /// ``` pub fn media_type(mut self, media_type: crate::params::pulls::MediaType) -> Self { self.media_type = Some(media_type); self } /// Checks if a given pull request has been merged. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); /// octocrab.pulls("owner", "repo").is_merged(101).await?; /// # Ok(()) /// # } /// ``` pub async fn is_merged(&self, pr: u64) -> crate::Result { let route = format!( "/repos/{owner}/{repo}/pulls/{pr}/merge", owner = self.owner, repo = self.repo, pr = pr ); let uri = Uri::builder() .path_and_query(route) .build() .context(crate::error::HttpSnafu)?; let response = self.crab._get(uri).await?; Ok(response.status() == 204) } /// Update the branch of a pull request. /// /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); /// octocrab.pulls("owner", "repo").update_branch(101).await?; /// # Ok(()) /// # } /// ``` pub async fn update_branch(&self, pr: u64) -> crate::Result { let route = format!( "/repos/{owner}/{repo}/pulls/{pr}/update-branch", owner = self.owner, repo = self.repo, pr = pr ); let uri = Uri::builder() .path_and_query(route) .build() .context(crate::error::HttpSnafu)?; let response = self.crab._put(uri, None::<&()>).await?; Ok(response.status() == 202) } /// Get's a given pull request with by its `pr` number. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let pr = octocrab::instance().pulls("owner", "repo").get(101).await?; /// # Ok(()) /// # } /// ``` pub async fn get(&self, pr: u64) -> crate::Result { let route = format!( "/repos/{owner}/{repo}/pulls/{pr}", owner = self.owner, repo = self.repo, pr = pr ); self.http_get(route, None::<&()>).await } /// Get's a given pull request's `diff`. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let diff = octocrab::instance().pulls("owner", "repo").get_diff(101).await?; /// # Ok(()) /// # } /// ``` pub async fn get_diff(&self, pr: u64) -> crate::Result { let route = format!( "/repos/{owner}/{repo}/pulls/{pr}", owner = self.owner, repo = self.repo, pr = pr ); let uri = Uri::builder() .path_and_query(route) .build() .context(crate::error::HttpSnafu)?; let request = Builder::new() .method(Method::GET) .uri(uri) .header(http::header::ACCEPT, crate::format_media_type("diff")); let request = self.crab.build_request(request, None::<&()>)?; let response = crate::map_github_error(self.crab.execute(request).await?).await?; self.crab.body_to_string(response).await } /// Get's a given pull request's patch. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let diff = octocrab::instance().pulls("owner", "repo").get_patch(101).await?; /// # Ok(()) /// # } /// ``` pub async fn get_patch(&self, pr: u64) -> crate::Result { let route = format!( "/repos/{owner}/{repo}/pulls/{pr}", owner = self.owner, repo = self.repo, pr = pr ); let uri = Uri::builder() .path_and_query(route) .build() .context(crate::error::HttpSnafu)?; let request = Builder::new() .method("GET") .uri(uri) .header(http::header::ACCEPT, crate::format_media_type("patch")); let request = self.crab.build_request(request, None::<&()>)?; let response = crate::map_github_error(self.crab.execute(request).await?).await?; self.crab.body_to_string(response).await } /// Create a new pull request. /// /// - `title` — The title of the new pull request. /// - `head` — The name of the branch where your changes are implemented. /// For cross-repository pull requests in the same network, namespace head /// with a user like this: `username:branch`. /// - `base` — The name of the branch you want the changes pulled into. This /// should be an existing branch on the current repository. You cannot /// submit a pull request to one repository that requests a merge to a /// base of another repository. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); /// let pr = octocrab /// .pulls("owner", "repo") /// .create("title", "head", "base") /// .body("hello world!") /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn create( &self, title: impl Into, head: impl Into, base: impl Into, ) -> create::CreatePullRequestBuilder<'octo, '_> { create::CreatePullRequestBuilder::new(self, title, head, base) } /// Update a new pull request. /// /// - `pull_number` — pull request number. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); /// let pr = octocrab /// .pulls("owner", "repo") /// .update(1) /// .body("hello world!") /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn update(&self, pull_number: u64) -> update::UpdatePullRequestBuilder<'octo, '_> { update::UpdatePullRequestBuilder::new(self, pull_number) } /// Creates a new `ListPullRequestsBuilder` that can be configured to filter /// listing pulling requests. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); /// use octocrab::params; /// /// let page = octocrab.pulls("owner", "repo").list() /// // Optional Parameters /// .state(params::State::Open) /// .head("master") /// .base("branch") /// .sort(params::pulls::Sort::Popularity) /// .direction(params::Direction::Ascending) /// .per_page(100) /// .page(5u32) /// // Send the request /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn list(&self) -> list::ListPullRequestsBuilder { list::ListPullRequestsBuilder::new(self) } /// Lists all of the `Review`s associated with the pull request. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let reviews = octocrab::instance() /// .pulls("owner", "repo") /// .list_reviews(21u64.into()) /// .per_page(100) /// .page(2u32) /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn list_reviews(&self, pr_number: u64) -> ListReviewsBuilder<'_, '_> { ListReviewsBuilder::new(self, pr_number) } /// Request a review from users or teams. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let review = octocrab::instance().pulls("owner", "repo") /// .request_reviews(101, ["user1".to_string(), "user2".to_string()], ["team1".to_string(), "team2".to_string()]) /// .await?; /// # Ok(()) /// # } /// ``` pub async fn request_reviews( &self, pr: u64, reviewers: impl Into>, team_reviewers: impl Into>, ) -> crate::Result { let route = format!( "/repos/{owner}/{repo}/pulls/{pr}/requested_reviewers", owner = self.owner, repo = self.repo, ); let mut map = serde_json::Map::new(); map.insert("reviewers".to_string(), reviewers.into().into()); map.insert("team_reviewers".to_string(), team_reviewers.into().into()); self.crab.post(route, Some(&map)).await } /// Remove a requested reviewer from users or teams. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let review = octocrab::instance().pulls("owner", "repo") /// .remove_requested_reviewers(101, ["user1".to_string(), "user2".to_string()], ["team1".to_string(), "team2".to_string()]) /// .await?; /// # Ok(()) /// # } /// ``` pub async fn remove_requested_reviewers( &self, pr: u64, reviewers: impl Into>, team_reviewers: impl Into>, ) -> crate::Result { let route = format!( "/repos/{owner}/{repo}/pulls/{pr}/requested_reviewers", owner = self.owner, repo = self.repo, ); let mut map = serde_json::Map::new(); map.insert("reviewers".to_string(), reviewers.into().into()); map.insert("team_reviewers".to_string(), team_reviewers.into().into()); self.crab.delete(route, Some(&map)).await } /// List all `FileDiff`s associated with the pull request. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let files = octocrab::instance().pulls("owner", "repo").list_files(101).await?; /// # Ok(()) /// # } pub async fn list_files(&self, pr: u64) -> crate::Result> { let route = format!( "/repos/{owner}/{repo}/pulls/{pr}/files", owner = self.owner, repo = self.repo, ); self.http_get(route, None::<&()>).await } /// Creates a new `ListCommentsBuilder` that can be configured to list and /// filter `Comments` for a particular pull request. If no pull request is /// specified, lists comments for the whole repo. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); /// use octocrab::params; /// /// let page = octocrab.pulls("owner", "repo").list_comments(Some(5)) /// // Optional Parameters /// .sort(params::pulls::comments::Sort::Created) /// .direction(params::Direction::Ascending) /// .per_page(100) /// .page(5u32) /// .since(chrono::Utc::now() - chrono::Duration::days(1)) /// // Send the request /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn list_comments(&self, pr: Option) -> comment::ListCommentsBuilder { comment::ListCommentsBuilder::new(self, pr) } /// Creates a new `MergePullRequestsBuilder` that can be configured used to /// merge a pull request. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); /// use octocrab::params; /// /// let page = octocrab.pulls("owner", "repo").merge(20) /// // Optional Parameters /// .title("cool title") /// .message("a message") /// // Won't merge of the HEAD commit of the PR branch is not the same /// .sha("0123456") /// // The method to use when merging, will default to `Merge` /// .method(params::pulls::MergeMethod::Squash) /// // Send the request /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn merge(&self, pr: u64) -> merge::MergePullRequestsBuilder { merge::MergePullRequestsBuilder::new(self, pr) } } impl<'octo, 'r> ListReviewsBuilder<'octo, 'r> { pub(crate) fn new(handler: &'r PullRequestHandler<'octo>, pr_number: u64) -> Self { Self { handler, pr_number, per_page: None, page: None, } } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Send the actual request. pub async fn send(self) -> crate::Result> { let route = format!( "/repos/{owner}/{repo}/pulls/{pr}/reviews", owner = self.handler.owner, repo = self.handler.repo, pr = self.pr_number, ); self.handler.crab.get(route, Some(&self)).await } } #[derive(serde::Serialize)] pub struct ListReviewsBuilder<'octo, 'r> { #[serde(skip)] handler: &'r PullRequestHandler<'octo>, #[serde(skip)] pr_number: u64, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo> PullRequestHandler<'octo> { pub(crate) async fn http_get( &self, route: A, parameters: Option<&P>, ) -> crate::Result where A: AsRef, P: serde::Serialize + ?Sized, R: crate::FromResponse, { let uri = self.crab.parameterized_uri(route, parameters)?; let mut request = Builder::new().uri(uri); if let Some(media_type) = self.media_type { request = request.header( http::header::ACCEPT, crate::format_media_type(media_type.to_string()), ); } let request = self.crab.build_request(request, None::<&()>)?; R::from_response(crate::map_github_error(self.crab.execute(request).await?).await?).await } pub(crate) async fn http_post(&self, route: A, body: Option<&P>) -> crate::Result where A: AsRef, P: serde::Serialize + ?Sized, R: crate::FromResponse, { let uri = Uri::builder() .path_and_query(route.as_ref()) .build() .context(HttpSnafu)?; let mut request = Builder::new().method(Method::POST).uri(uri); request = self.build_request(request); let request = self.crab.build_request(request, body)?; R::from_response(crate::map_github_error(self.crab.execute(request).await?).await?).await } pub(crate) async fn http_put(&self, route: A, body: Option<&P>) -> crate::Result where A: AsRef, P: serde::Serialize + ?Sized, R: crate::FromResponse, { let uri = Uri::builder() .path_and_query(route.as_ref()) .build() .context(HttpSnafu)?; let mut request = Builder::new().method(Method::PUT).uri(uri); request = self.build_request(request); let request = self.crab.build_request(request, body)?; R::from_response(crate::map_github_error(self.crab.execute(request).await?).await?).await } pub(crate) async fn http_patch(&self, route: A, body: Option<&P>) -> crate::Result where A: AsRef, P: serde::Serialize + ?Sized, R: crate::FromResponse, { let uri = Uri::builder() .path_and_query(route.as_ref()) .build() .context(HttpSnafu)?; let mut request = Builder::new().method(Method::PATCH).uri(uri); request = self.build_request(request); let request = self.crab.build_request(request, body)?; R::from_response(crate::map_github_error(self.crab.execute(request).await?).await?).await } fn build_request(&self, mut request: http::request::Builder) -> http::request::Builder where { if let Some(media_type) = self.media_type { request = request.header( http::header::ACCEPT, crate::format_media_type(media_type.to_string()), ); } request } } octocrab-0.31.2/src/api/ratelimit.rs000064400000000000000000000013111046102023000153770ustar 00000000000000//! Github RateLimit API use crate::{models, Octocrab, Result}; /// Handler for GitHub's rate_limit API. /// /// Created with [`Octocrab::ratelimit`]. pub struct RateLimitHandler<'octo> { crab: &'octo Octocrab, } impl<'octo> RateLimitHandler<'octo> { pub(crate) fn new(crab: &'octo Octocrab) -> Self { Self { crab } } /// Get the rate limit. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let ratelimit = octocrab::instance() /// .ratelimit() /// .get() /// .await?; /// # Ok(()) /// # } /// ``` pub async fn get(&self) -> Result { self.crab.get("/rate_limit", None::<&()>).await } } octocrab-0.31.2/src/api/repos/branches.rs000064400000000000000000000031121046102023000163230ustar 00000000000000use super::*; #[derive(serde::Serialize)] pub struct ListBranchesBuilder<'octo, 'r> { #[serde(skip)] handler: &'r RepoHandler<'octo>, #[serde(skip_serializing_if = "Option::is_none")] protected: Option, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo, 'r> ListBranchesBuilder<'octo, 'r> { pub fn new(handler: &'r RepoHandler<'octo>) -> Self { Self { handler, protected: None, per_page: None, page: None, } } /// Setting to true returns only protected branches. When set to false, only /// unprotected branches are returned. Omitting this parameter returns all /// branches. pub fn protected(mut self, protected: impl Into) -> Self { self.protected = Some(protected.into()); self } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Sends the actual request. pub async fn send(self) -> Result> { let route = format!( "/repos/{owner}/{repo}/branches", owner = self.handler.owner, repo = self.handler.repo ); self.handler.crab.get(route, Some(&self)).await } } octocrab-0.31.2/src/api/repos/collaborators.rs000064400000000000000000000022721046102023000174120ustar 00000000000000use super::*; #[derive(serde::Serialize)] pub struct ListCollaboratorsBuilder<'octo, 'r> { #[serde(skip)] handler: &'r RepoHandler<'octo>, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo, 'r> ListCollaboratorsBuilder<'octo, 'r> { pub fn new(handler: &'r RepoHandler<'octo>) -> Self { Self { handler, per_page: None, page: None, } } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Sends the actual request. pub async fn send(self) -> crate::Result> { let route = format!( "/repos/{owner}/{repo}/collaborators", owner = self.handler.owner, repo = self.handler.repo ); self.handler.crab.get(route, Some(&self)).await } } octocrab-0.31.2/src/api/repos/commits.rs000064400000000000000000000056721046102023000162260ustar 00000000000000use super::*; use chrono::{DateTime, Utc}; #[derive(serde::Serialize)] pub struct ListCommitsBuilder<'octo, 'r> { #[serde(skip)] handler: &'r RepoHandler<'octo>, #[serde(skip_serializing_if = "Option::is_none")] sha: Option, #[serde(skip_serializing_if = "Option::is_none")] path: Option, #[serde(skip_serializing_if = "Option::is_none")] author: Option, #[serde(skip_serializing_if = "Option::is_none")] since: Option>, #[serde(skip_serializing_if = "Option::is_none")] until: Option>, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo, 'r> ListCommitsBuilder<'octo, 'r> { pub(crate) fn new(handler: &'r RepoHandler<'octo>) -> Self { Self { handler, sha: None, path: None, author: None, since: None, until: None, per_page: None, page: None, } } /// SHA or branch to start listing commits from. Default: the repository’s default /// branch (usually master). pub fn sha(mut self, sha: impl Into) -> Self { self.sha = Some(sha.into()); self } /// Alias for [`ListCommitsBuilder::sha`], setting a branch will replace the SHA or vice versa. pub fn branch(mut self, branch: impl Into) -> Self { self.sha = Some(branch.into()); self } /// Only commits containing this file path will be returned. pub fn path(mut self, path: impl Into) -> Self { self.path = Some(path.into()); self } /// GitHub login or email address by which to filter by commit author. pub fn author(mut self, author: impl Into) -> Self { self.author = Some(author.into()); self } /// Only show notifications updated after the given time. pub fn since(mut self, since: DateTime) -> Self { self.since = Some(since); self } /// Only commits before this date will be returned. pub fn until(mut self, until: DateTime) -> Self { self.until = Some(until); self } /// Results per page (max: 100, default: 30). pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. (default: 1) pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Sends the actual request. pub async fn send(self) -> crate::Result> { let route = format!( "/repos/{owner}/{repo}/commits", owner = self.handler.owner, repo = self.handler.repo ); self.handler.crab.get(route, Some(&self)).await } } octocrab-0.31.2/src/api/repos/events.rs000064400000000000000000000053311046102023000160470ustar 00000000000000//! GitHub Repository Events use crate::{ etag::{EntityTag, Etagged}, models::events, repos::RepoHandler, FromResponse, Page, }; use http::request::Builder; use http::{header::HeaderMap, Method, StatusCode}; pub struct ListRepoEventsBuilder<'octo, 'handler> { handler: &'handler RepoHandler<'octo>, headers: Headers, params: Params, } struct Headers { etag: Option, } #[derive(serde::Serialize)] struct Params { #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo, 'handler> ListRepoEventsBuilder<'octo, 'handler> { pub(crate) fn new(handler: &'handler RepoHandler<'octo>) -> Self { Self { handler, headers: Headers { etag: None }, params: Params { per_page: None, page: None, }, } } /// Etag for this request. pub fn etag(mut self, etag: Option) -> Self { self.headers.etag = etag; self } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.params.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.params.page = Some(page.into()); self } /// Sends the actual request. pub async fn send(self) -> crate::Result>> { let route = format!( "/repos/{owner}/{repo}/events", owner = self.handler.owner, repo = self.handler.repo ); let uri = self .handler .crab .parameterized_uri(route, Some(&self.params))?; let mut headers = HeaderMap::new(); if let Some(etag) = self.headers.etag { EntityTag::insert_if_none_match_header(&mut headers, etag)?; } let mut request = Builder::new().uri(uri).method(Method::GET); for (key, value) in headers.iter() { request = request.header(key, value); } let request = self.handler.crab.build_request(request, None::<&()>)?; let response = self.handler.crab.execute(request).await?; let etag = EntityTag::extract_from_response(&response); if response.status() == StatusCode::NOT_MODIFIED { Ok(Etagged { etag, value: None }) } else { >::from_response(crate::map_github_error(response).await?) .await .map(|page| Etagged { etag, value: Some(page), }) } } } octocrab-0.31.2/src/api/repos/file.rs000064400000000000000000000212631046102023000154640ustar 00000000000000use super::*; #[derive(serde::Serialize)] pub struct GetContentBuilder<'octo, 'r> { #[serde(skip)] handler: &'r RepoHandler<'octo>, #[serde(skip)] path: Option, #[serde(skip_serializing_if = "Option::is_none")] r#ref: Option, } impl<'octo, 'r> GetContentBuilder<'octo, 'r> { pub(crate) fn new(handler: &'r RepoHandler<'octo>) -> Self { Self { handler, path: None, r#ref: None, } } /// The content path. pub fn path(mut self, path: impl Into) -> Self { self.path = Some(path.into()); self } /// The name of the commit/branch/tag. /// Default: the repository’s default branch (usually `master) pub fn r#ref(mut self, r#ref: impl Into) -> Self { self.r#ref = Some(r#ref.into()); self } /// Sends the actual request. pub async fn send(self) -> Result { let path = self.path.clone().unwrap_or(String::from("")); let route = format!( "/repos/{owner}/{repo}/contents/{path}", owner = self.handler.owner, repo = self.handler.repo, path = path, ); self.handler.crab.get(route, Some(&self)).await } } #[derive(serde::Serialize)] pub struct GetReadmeBuilder<'octo, 'r> { #[serde(skip)] handler: &'r RepoHandler<'octo>, #[serde(skip)] path: Option, #[serde(skip_serializing_if = "Option::is_none")] r#ref: Option, } impl<'octo, 'r> GetReadmeBuilder<'octo, 'r> { pub(crate) fn new(handler: &'r RepoHandler<'octo>) -> Self { Self { handler, path: None, r#ref: None, } } /// The content path. /// Default: none (the repository's root directory) pub fn path(mut self, path: impl Into) -> Self { self.path = Some(path.into()); self } /// The name of the commit/branch/tag. /// Default: the repository’s default branch (usually `master) pub fn r#ref(mut self, r#ref: impl Into) -> Self { self.r#ref = Some(r#ref.into()); self } /// Sends the actual request. pub async fn send(self) -> Result { let path = self.path.clone().unwrap_or(String::from("")); let route = format!( "/repos/{owner}/{repo}/readme/{path}", owner = self.handler.owner, repo = self.handler.repo, path = path, ); self.handler.crab.get(route, Some(&self)).await } } #[derive(serde::Serialize)] pub struct UpdateFileBuilder<'octo, 'r> { #[serde(skip)] handler: &'r RepoHandler<'octo>, #[serde(skip)] path: String, message: String, content: String, #[serde(skip_serializing_if = "Option::is_none")] sha: Option, #[serde(skip_serializing_if = "Option::is_none")] branch: Option, #[serde(skip_serializing_if = "Option::is_none")] commiter: Option, #[serde(skip_serializing_if = "Option::is_none")] author: Option, } impl<'octo, 'r> UpdateFileBuilder<'octo, 'r> { pub(crate) fn new( handler: &'r RepoHandler<'octo>, path: String, message: String, content: String, sha: Option, ) -> Self { Self { handler, path, message, content, sha, branch: None, commiter: None, author: None, } } /// The branch to commit to. pub fn branch(mut self, branch: impl Into) -> Self { self.branch = Some(branch.into()); self } /// The person that commited the file. pub fn commiter(mut self, commiter: impl Into) -> Self { self.commiter = Some(commiter.into()); self } /// The author of the file. pub fn author(mut self, author: impl Into) -> Self { self.author = Some(author.into()); self } /// Sends the actual request. pub async fn send(self) -> Result { let route = format!( "/repos/{owner}/{repo}/contents/{path}", owner = self.handler.owner, repo = self.handler.repo, path = self.path, ); self.handler.crab.put(route, Some(&self)).await } } #[derive(serde::Serialize)] pub struct DeleteFileBuilder<'octo, 'r> { #[serde(skip)] handler: &'r RepoHandler<'octo>, #[serde(skip)] path: String, message: String, sha: String, #[serde(skip_serializing_if = "Option::is_none")] branch: Option, #[serde(skip_serializing_if = "Option::is_none")] commiter: Option, #[serde(skip_serializing_if = "Option::is_none")] author: Option, } impl<'octo, 'r> DeleteFileBuilder<'octo, 'r> { pub(crate) fn new( handler: &'r RepoHandler<'octo>, path: String, message: String, sha: String, ) -> Self { Self { handler, path, message, sha, branch: None, commiter: None, author: None, } } /// The branch to commit to. pub fn branch(mut self, branch: impl Into) -> Self { self.branch = Some(branch.into()); self } /// The person that commited the file. pub fn commiter(mut self, commiter: impl Into) -> Self { self.commiter = Some(commiter.into()); self } /// The author of the file. pub fn author(mut self, author: impl Into) -> Self { self.author = Some(author.into()); self } /// Sends the actual request. pub async fn send(self) -> Result { let route = format!( "/repos/{owner}/{repo}/contents/{path}", owner = self.handler.owner, repo = self.handler.repo, path = self.path, ); self.handler.crab.delete(route, Some(&self)).await } } #[cfg(test)] mod tests { use crate::models::repos::CommitAuthor; #[tokio::test] async fn serialize() { let octocrab = crate::instance(); let repo = octocrab.repos("owner", "repo"); let builder = repo .update_file( "tests/test.txt", "Update test.txt", "This is a test.", "testsha", ) .branch("not-master") .commiter(CommitAuthor { name: "Octocat".to_string(), email: "octocat@github.com".to_string(), }) .author(CommitAuthor { name: "Ferris".to_string(), email: "ferris@rust-lang.org".to_string(), }); use base64::{engine::general_purpose, Engine as _}; assert_eq!( serde_json::to_value(builder).unwrap(), serde_json::json!({ "message": "Update test.txt", "content": general_purpose::STANDARD.encode("This is a test."), "sha": "testsha", "branch": "not-master", "commiter": { "name": "Octocat", "email": "octocat@github.com" }, "author": { "name": "Ferris", "email": "ferris@rust-lang.org" } }) ) } #[tokio::test] async fn serialize_delete() { let octocrab = crate::instance(); let repo = octocrab.repos("owner", "repo"); let builder = repo .delete_file("tests/test.txt", "Update test.txt", "testsha") .branch("not-master") .commiter(CommitAuthor { name: "Octocat".to_string(), email: "octocat@github.com".to_string(), }) .author(CommitAuthor { name: "Ferris".to_string(), email: "ferris@rust-lang.org".to_string(), }); assert_eq!( serde_json::to_value(builder).unwrap(), serde_json::json!({ "message": "Update test.txt", "sha": "testsha", "branch": "not-master", "commiter": { "name": "Octocat", "email": "octocat@github.com" }, "author": { "name": "Ferris", "email": "ferris@rust-lang.org" } }) ) } } octocrab-0.31.2/src/api/repos/forks.rs000064400000000000000000000073261046102023000156750ustar 00000000000000use super::params::repos::forks::Sort; use super::*; #[derive(serde::Serialize)] pub struct ListForksBuilder<'octo, 'r> { #[serde(skip)] handler: &'r RepoHandler<'octo>, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, #[serde(skip_serializing_if = "Option::is_none")] sort: Option, } impl<'octo, 'r> ListForksBuilder<'octo, 'r> { pub(crate) fn new(handler: &'r RepoHandler<'octo>) -> Self { Self { handler, per_page: None, page: None, sort: None, } } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Sort order of the results. pub fn sort(mut self, sort: Sort) -> Self { self.sort = Some(sort); self } /// Sends the actual request. pub async fn send(self) -> crate::Result> { let route = format!( "/repos/{owner}/{repo}/forks", owner = self.handler.owner, repo = self.handler.repo ); self.handler.crab.get(route, Some(&self)).await } } #[derive(serde::Serialize)] pub struct CreateForkBuilder<'octo, 'r> { #[serde(skip)] handler: &'r RepoHandler<'octo>, #[serde(skip_serializing_if = "Option::is_none")] organization: Option, } impl<'octo, 'r> CreateForkBuilder<'octo, 'r> { pub(crate) fn new(handler: &'r RepoHandler<'octo>) -> Self { Self { handler, organization: None, } } /// The organization name, if forking into an organization. pub fn organization(mut self, organization: impl Into) -> Self { self.organization = Some(organization.into()); self } /// Sends the actual request. pub async fn send(self) -> crate::Result { let route = format!( "/repos/{owner}/{repo}/forks", owner = self.handler.owner, repo = self.handler.repo ); self.handler.crab.post(route, Some(&self)).await } } impl<'octo> RepoHandler<'octo> { /// List forks of a repository. Optionally, specify the /// [sort](ListForksBuilder::sort()) order, /// [page](ListForksBuilder::page()), /// and items [per_page](ListForksBuilder::per_page()) /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// use octocrab::params::repos::forks::Sort; /// let forks = octocrab::instance() /// .repos("owner", "repo") /// .list_forks() /// // Optional Parameters /// .sort(Sort::Oldest) /// .page(2u32) /// .per_page(30) /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn list_forks(&self) -> ListForksBuilder<'_, '_> { ListForksBuilder::new(self) } /// Creates a fork of a repository. Optionally, specify the target /// [organization](CreateForkBuilder::organization()) to /// create the fork in. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let new_fork = octocrab::instance() /// .repos("owner", "repo") /// .create_fork() /// // Optional Parameters /// .organization("weyland-yutani") /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn create_fork(&self) -> CreateForkBuilder<'_, '_> { CreateForkBuilder::new(self) } } octocrab-0.31.2/src/api/repos/generate.rs000064400000000000000000000050401046102023000163320ustar 00000000000000use crate::error::HttpSnafu; use crate::{repos::RepoHandler, Error}; use http::request::Builder; use http::Uri; use snafu::ResultExt; #[derive(serde::Serialize)] pub struct GenerateRepositoryBuilder<'octo, 'r> { #[serde(skip)] handler: &'r RepoHandler<'octo>, name: String, #[serde(skip_serializing_if = "Option::is_none")] owner: Option, #[serde(skip_serializing_if = "Option::is_none")] description: Option, #[serde(skip_serializing_if = "Option::is_none")] include_all_branches: Option, #[serde(skip_serializing_if = "Option::is_none")] private: Option, } impl<'octo, 'r> GenerateRepositoryBuilder<'octo, 'r> { pub(crate) fn new(handler: &'r RepoHandler<'octo>, name: impl Into) -> Self { Self { handler, name: name.into(), owner: None, description: None, include_all_branches: None, private: None, } } /// New owner of the newly created repository from selected template. pub fn owner(mut self, owner: impl Into) -> Self { self.owner = Some(owner.into()); self } /// Description of the newly created repository. pub fn description(mut self, description: impl Into) -> Self { self.description = Some(description.into()); self } /// Whether to include all branches from template repository. pub fn include_all_branches(mut self, include_all_branches: impl Into) -> Self { self.include_all_branches = Some(include_all_branches.into()); self } /// Whether to set newly created repository to private . pub fn private(mut self, private: impl Into) -> Self { self.private = Some(private.into()); self } /// Sends the actual request. pub async fn send(self) -> Result<(), Error> { let route = format!( "/repos/{owner}/{repo}/generate", owner = self.handler.owner, repo = self.handler.repo ); let uri = Uri::builder() .path_and_query(route) .build() .context(HttpSnafu)?; let request = Builder::new().uri(uri).method(http::Method::POST).header( http::header::ACCEPT, "application/vnd.github.baptiste-preview+json", ); let request = self.handler.crab.build_request(request, Some(&self))?; let response = self.handler.crab.execute(request).await?; crate::map_github_error(response).await.map(drop) } } octocrab-0.31.2/src/api/repos/merges.rs000064400000000000000000000021701046102023000160230ustar 00000000000000use super::*; #[derive(serde::Serialize)] pub struct MergeBranchBuilder<'octo, 'r> { #[serde(skip)] handler: &'r RepoHandler<'octo>, head: String, base: String, #[serde(skip_serializing_if = "Option::is_none")] commit_message: Option, } impl<'octo, 'r> MergeBranchBuilder<'octo, 'r> { pub fn new( handler: &'r RepoHandler<'octo>, head: impl Into, base: impl Into, ) -> Self { Self { handler, head: head.into(), base: base.into(), commit_message: None, } } /// The message to use for the merge commit. pub fn commit_message(mut self, commit_message: impl Into) -> Self { self.commit_message = Some(commit_message.into()); self } /// Sends the actual request. pub async fn send(self) -> Result { let route = format!( "/repos/{owner}/{repo}/merges", owner = self.handler.owner, repo = self.handler.repo ); self.handler.crab.post(route, Some(&self)).await } } octocrab-0.31.2/src/api/repos/pulls.rs000064400000000000000000000024251046102023000157030ustar 00000000000000use super::*; #[derive(serde::Serialize)] pub struct ListPullsBuilder<'octo, 'r> { #[serde(skip)] handler: &'r RepoHandler<'octo>, #[serde(skip)] sha: String, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo, 'r> ListPullsBuilder<'octo, 'r> { pub fn new(handler: &'r RepoHandler<'octo>, sha: String) -> Self { Self { handler, sha, per_page: None, page: None, } } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Sends the actual request. pub async fn send(self) -> crate::Result> { let route = format!( "/repos/{owner}/{repo}/commits/{sha}/pulls", owner = self.handler.owner, repo = self.handler.repo, sha = self.sha, ); self.handler.crab.get(route, Some(&self)).await } } octocrab-0.31.2/src/api/repos/releases.rs000064400000000000000000000345251046102023000163550ustar 00000000000000use crate::models::AssetId; use super::*; /// Handler for GitHub's releases API. /// /// Created with [`RepoHandler::releases`]. pub struct ReleasesHandler<'octo, 'r> { parent: &'r RepoHandler<'octo>, } impl<'octo, 'r> ReleasesHandler<'octo, 'r> { pub(crate) fn new(parent: &'r RepoHandler<'octo>) -> Self { Self { parent } } /// Creates a new [`ListReleasesBuilder`] that can be configured to filter /// listing releases. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); /// let page = octocrab.repos("owner", "repo") /// .releases() /// .list() /// // Optional Parameters /// .per_page(100) /// .page(5u32) /// // Send the request /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn list(&self) -> ListReleasesBuilder<'_, '_, '_> { ListReleasesBuilder::new(self) } /// Creates a new [`CreateReleaseBuilder`] with `tag_name`. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); /// let page = octocrab.repos("owner", "repo") /// .releases() /// .create("v1.0.0") /// // Optional Parameters /// .target_commitish("main") /// .name("Version 1.0.0") /// .body("Announcing 1.0.0!") /// .draft(false) /// .prerelease(false) /// // Send the request /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn create<'t>( &self, tag_name: &'t (impl AsRef + ?Sized), ) -> CreateReleaseBuilder<'_, '_, '_, 't, '_, '_, '_> { CreateReleaseBuilder::new(self, tag_name.as_ref()) } /// Creates a new [`UpdateReleaseBuilder`] with `release_id`. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); /// let release = octocrab.repos("owner", "repo") /// .releases() /// .update(1) /// // Optional Parameters /// .tag_name("v1.0.0") /// .target_commitish("main") /// .name("Version 1.0.0") /// .body("Announcing 1.0.0!") /// .draft(false) /// .prerelease(false) /// // Send the request /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn update(&self, release_id: u64) -> UpdateReleaseBuilder<'_, '_, '_, '_, '_, '_, '_> { UpdateReleaseBuilder::new(self, release_id) } /// Fetches a single asset by its ID. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let asset = octocrab::instance() /// .repos("owner", "repo") /// .releases() /// .get_asset(42u64.into()) /// .await?; /// # Ok(()) /// # } /// ``` pub async fn get_asset(&self, asset_id: AssetId) -> crate::Result { let route = format!( "/repos/{owner}/{repo}/releases/assets/{asset_id}", owner = self.parent.owner, repo = self.parent.repo, asset_id = asset_id, ); self.parent.crab.get(route, None::<&()>).await } /// Gets the latest release. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let release = octocrab::instance() /// .repos("owner", "repo") /// .releases() /// .get_latest() /// .await?; /// # Ok(()) /// # } /// ``` pub async fn get_latest(&self) -> crate::Result { let route = format!( "/repos/{owner}/{repo}/releases/latest", owner = self.parent.owner, repo = self.parent.repo, ); self.parent.crab.get(route, None::<&()>).await } /// Gets the release using its tag. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let release = octocrab::instance() /// .repos("owner", "repo") /// .releases() /// .get_by_tag("v1.0.0") /// .await?; /// # Ok(()) /// # } /// ``` pub async fn get_by_tag(&self, tag: &str) -> crate::Result { let route = format!( "/repos/{owner}/{repo}/releases/tags/{tag}", owner = self.parent.owner, repo = self.parent.repo, tag = tag, ); self.parent.crab.get(route, None::<&()>).await } /// Streams the binary contents of an asset. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// use futures_util::StreamExt; /// /// let mut stream = octocrab::instance() /// .repos("owner", "repo") /// .releases() /// .stream_asset(42u64.into()) /// .await?; /// /// while let Some(chunk) = stream.next().await { /// println!("{:?}", chunk); /// } /// # Ok(()) /// # } /// ``` #[cfg(feature = "stream")] #[cfg_attr(docsrs, doc(cfg(feature = "stream")))] pub async fn stream_asset( &self, asset_id: AssetId, ) -> crate::Result>> { use futures_util::TryStreamExt; use snafu::GenerateImplicitData; let route = format!( "/repos/{owner}/{repo}/releases/assets/{asset_id}", owner = self.parent.owner, repo = self.parent.repo, asset_id = asset_id, ); let uri = Uri::builder() .path_and_query(route) .build() .context(HttpSnafu)?; let builder = Builder::new() .method(http::Method::GET) .uri(uri) .header(http::header::ACCEPT, "application/octet-stream"); let request = self.parent.crab.build_request(builder, None::<&()>)?; let response = self.parent.crab.execute(request).await?; Ok(response .into_body() .map_err(|source| crate::error::Error::Hyper { source, backtrace: snafu::Backtrace::generate(), })) } } /// A builder pattern struct for listing releases. /// /// created by [`ReleasesHandler::list`] #[derive(serde::Serialize)] pub struct ListReleasesBuilder<'octo, 'r1, 'r2> { #[serde(skip)] handler: &'r2 ReleasesHandler<'octo, 'r1>, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo, 'r1, 'r2> ListReleasesBuilder<'octo, 'r1, 'r2> { pub(crate) fn new(handler: &'r2 ReleasesHandler<'octo, 'r1>) -> Self { Self { handler, per_page: None, page: None, } } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Sends the actual request. pub async fn send(self) -> crate::Result> { let route = format!( "/repos/{owner}/{repo}/releases", owner = self.handler.parent.owner, repo = self.handler.parent.repo ); self.handler.parent.crab.get(route, Some(&self)).await } } /// A builder pattern struct for creating releases. /// /// created by [`ReleasesHandler::create`]. #[derive(serde::Serialize)] pub struct CreateReleaseBuilder<'octo, 'repos, 'handler, 'tag_name, 'target_commitish, 'name, 'body> { #[serde(skip)] handler: &'handler ReleasesHandler<'octo, 'repos>, tag_name: &'tag_name str, #[serde(skip_serializing_if = "Option::is_none")] target_commitish: Option<&'target_commitish str>, #[serde(skip_serializing_if = "Option::is_none")] name: Option<&'name str>, #[serde(skip_serializing_if = "Option::is_none")] body: Option<&'body str>, #[serde(skip_serializing_if = "Option::is_none")] draft: Option, #[serde(skip_serializing_if = "Option::is_none")] prerelease: Option, #[serde(skip_serializing_if = "Option::is_none")] make_latest: Option, } #[derive(Debug, Clone, Copy, serde::Serialize)] #[serde(rename_all = "lowercase")] pub enum MakeLatest { True, False, Legacy, } impl std::fmt::Display for MakeLatest { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { let text = match self { Self::False => "false", Self::True => "true", Self::Legacy => "legacy", }; f.write_str(text) } } impl<'octo, 'repos, 'handler, 'tag_name, 'target_commitish, 'name, 'body> CreateReleaseBuilder<'octo, 'repos, 'handler, 'tag_name, 'target_commitish, 'name, 'body> { pub(crate) fn new( handler: &'handler ReleasesHandler<'octo, 'repos>, tag_name: &'tag_name str, ) -> Self { Self { handler, tag_name, target_commitish: None, name: None, body: None, draft: None, prerelease: None, make_latest: None, } } /// Specifies the commitish value that determines where the Git tag is /// created from. Can be any branch or commit SHA. Unused if the Git tag /// already exists. Default: the repository's default branch /// (usually `main`). pub fn target_commitish( mut self, target_commitish: &'target_commitish (impl AsRef + ?Sized), ) -> Self { self.target_commitish = Some(target_commitish.as_ref()); self } /// The name of the release. pub fn name(mut self, name: &'name (impl AsRef + ?Sized)) -> Self { self.name = Some(name.as_ref()); self } /// Text describing the contents of the tag. pub fn body(mut self, body: &'body (impl AsRef + ?Sized)) -> Self { self.body = Some(body.as_ref()); self } /// Whether to set the release as a "draft" release or not. pub fn draft(mut self, draft: impl Into) -> Self { self.draft = Some(draft.into()); self } /// Whether to set the release as a "prerelease" or not. pub fn prerelease(mut self, prerelease: impl Into) -> Self { self.prerelease = Some(prerelease.into()); self } /// Specifies whether this release should be set as the latest release for the repository. /// Drafts and prereleases cannot be set as latest. /// Defaults to [`MakeLatest::True`] for newly published releases. /// [`MakeLatest::Legacy`] specifies that the latest release should be determined based on the release creation date and higher semantic version. pub fn make_latest(mut self, make_latest: MakeLatest) -> Self { self.make_latest = Some(make_latest); self } /// Sends the actual request. pub async fn send(self) -> crate::Result { let route = format!( "/repos/{owner}/{repo}/releases", owner = self.handler.parent.owner, repo = self.handler.parent.repo ); self.handler.parent.crab.post(route, Some(&self)).await } } /// A builder pattern struct for updating releases. /// /// created by [`ReleasesHandler::update`]. #[derive(serde::Serialize)] pub struct UpdateReleaseBuilder<'octo, 'repos, 'handler, 'tag_name, 'target_commitish, 'name, 'body> { #[serde(skip)] handler: &'handler ReleasesHandler<'octo, 'repos>, release_id: u64, #[serde(skip_serializing_if = "Option::is_none")] tag_name: Option<&'tag_name str>, #[serde(skip_serializing_if = "Option::is_none")] target_commitish: Option<&'target_commitish str>, #[serde(skip_serializing_if = "Option::is_none")] name: Option<&'name str>, #[serde(skip_serializing_if = "Option::is_none")] body: Option<&'body str>, #[serde(skip_serializing_if = "Option::is_none")] draft: Option, #[serde(skip_serializing_if = "Option::is_none")] prerelease: Option, } impl<'octo, 'repos, 'handler, 'tag_name, 'target_commitish, 'name, 'body> UpdateReleaseBuilder<'octo, 'repos, 'handler, 'tag_name, 'target_commitish, 'name, 'body> { pub(crate) fn new(handler: &'handler ReleasesHandler<'octo, 'repos>, release_id: u64) -> Self { Self { handler, release_id, tag_name: None, target_commitish: None, name: None, body: None, draft: None, prerelease: None, } } /// The release tag name. pub fn tag_name(mut self, tag_name: &'tag_name (impl AsRef + ?Sized)) -> Self { self.tag_name = Some(tag_name.as_ref()); self } /// Specifies the commitish value that determines where the Git tag is /// created from. Can be any branch or commit SHA. Unused if the Git tag /// already exists. Default: the repository's default branch /// (usually `main`). pub fn target_commitish( mut self, target_commitish: &'target_commitish (impl AsRef + ?Sized), ) -> Self { self.target_commitish = Some(target_commitish.as_ref()); self } /// The name of the release. pub fn name(mut self, name: &'name (impl AsRef + ?Sized)) -> Self { self.name = Some(name.as_ref()); self } /// Text describing the contents of the tag. pub fn body(mut self, body: &'body (impl AsRef + ?Sized)) -> Self { self.body = Some(body.as_ref()); self } /// Whether to set the release as a "draft" release or not. pub fn draft(mut self, draft: impl Into) -> Self { self.draft = Some(draft.into()); self } /// Whether to set the release as a "prerelease" or not. pub fn prerelease(mut self, prerelease: impl Into) -> Self { self.prerelease = Some(prerelease.into()); self } /// Sends the actual request. pub async fn send(self) -> crate::Result { let route = format!( "/repos/{owner}/{repo}/releases/{release_id}", owner = self.handler.parent.owner, repo = self.handler.parent.repo, release_id = self.release_id, ); self.handler.parent.crab.patch(route, Some(&self)).await } } octocrab-0.31.2/src/api/repos/secrets.rs000064400000000000000000000146611046102023000162210ustar 00000000000000use http::StatusCode; use snafu::GenerateImplicitData; use super::RepoHandler; use crate::models::repos::secrets::{CreateRepositorySecret, CreateRepositorySecretResponse}; /// A client to GitHub's repository secrets API. /// /// Created with [`Octocrab::repos`]. pub struct RepoSecretsHandler<'octo> { repo: &'octo RepoHandler<'octo>, } impl<'octo> RepoSecretsHandler<'octo> { pub(crate) fn new(repo: &'octo RepoHandler<'octo>) -> Self { Self { repo } } /// Lists all secrets available in a repository without revealing their encrypted values. /// You must authenticate using an access token with the `repo` scope to use this endpoint. /// GitHub Apps must have the `secrets` repository permission to use this endpoint. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); /// let all_secrets = octocrab.repos("owner", "repo") /// .secrets() /// .get_secrets() /// .await?; /// # Ok(()) /// # } pub async fn get_secrets( &self, ) -> crate::Result { let route = format!( "/repos/{owner}/{repo}/actions/secrets", owner = self.repo.owner, repo = self.repo.repo ); self.repo.crab.get(route, None::<&()>).await } /// Gets your public key, which you need to encrypt secrets. /// You need to encrypt a secret before you can create or update secrets. /// Anyone with read access to the repository can use this endpoint. /// If the repository is private you must use an access token with the `repo` scope. /// GitHub Apps must have the `secrets` repository permission to use this endpoint. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); /// let public_key = octocrab.repos("owner", "repo") /// .secrets() /// .get_public_key() /// .await?; /// # Ok(()) /// # } pub async fn get_public_key(&self) -> crate::Result { let route = format!( "/repos/{owner}/{repo}/actions/secrets/public-key", owner = self.repo.owner, repo = self.repo.repo ); self.repo.crab.get(route, None::<&()>).await } /// Gets a single repository secret without revealing its encrypted value. /// You must authenticate using an access token with the `repo` scope to use this endpoint. /// GitHub Apps must have the `secrets` repository permission to use this endpoint. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); /// let secret_info = octocrab.repos("owner", "repo") /// .secrets() /// .get_secret("TOKEN") /// .await?; /// # Ok(()) /// # } pub async fn get_secret( &self, secret_name: impl AsRef, ) -> crate::Result { let route = format!( "/repos/{owner}/{repo}/actions/secrets/{secret_name}", owner = self.repo.owner, repo = self.repo.repo, secret_name = secret_name.as_ref() ); self.repo.crab.get(route, None::<&()>).await } /// Creates or updates a repository secret with an encrypted value. /// Encrypt your secret using [`crypto_box`](https://crates.io/crates/crypto_box). /// You must authenticate using an access token with the `repo` scope to use this endpoint. /// GitHub Apps must have the `secrets` repository permission to use this endpoint. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); /// use octocrab::models::repos::secrets::{CreateRepositorySecret, CreateRepositorySecretResponse}; /// /// let result = octocrab.repos("owner", "repo") /// .secrets() /// .create_or_update_secret("GH_TOKEN", &CreateRepositorySecret{ /// key_id: "123456", /// encrypted_value: "some-b64-encrypted-string", /// }) /// .await?; /// /// match result { /// CreateRepositorySecretResponse::Created => println!("Created secret!"), /// CreateRepositorySecretResponse::Updated => println!("Updated secret!"), /// } /// # Ok(()) /// # } pub async fn create_or_update_secret( &self, secret_name: impl AsRef, secret: &CreateRepositorySecret<'_>, ) -> crate::Result { let route = format!( "/repos/{owner}/{repo}/actions/secrets/{secret_name}", owner = self.repo.owner, repo = self.repo.repo, secret_name = secret_name.as_ref() ); let resp = { let resp = self.repo.crab._put(route, Some(secret)).await?; crate::map_github_error(resp).await? }; match resp.status() { StatusCode::CREATED => Ok(CreateRepositorySecretResponse::Created), StatusCode::NO_CONTENT => Ok(CreateRepositorySecretResponse::Updated), status_code => Err(crate::Error::Other { source: format!( "Unexpected status code from request: {}", status_code.as_str() ) .into(), backtrace: snafu::Backtrace::generate(), }), } } /// Deletes a secret in an organization using the secret name. /// You must authenticate using an access token with the `admin:org` scope to use this endpoint. /// GitHub Apps must have the `secrets` organization permission to use this endpoint. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); /// let repo = octocrab.repos("owner", "repo") /// .secrets() /// .delete_secret("GH_TOKEN") /// .await?; /// /// # Ok(()) /// # } pub async fn delete_secret(&self, secret_name: impl AsRef) -> crate::Result<()> { let route = format!( "/repos/{owner}/{repo}/actions/secrets/{secret_name}", owner = self.repo.owner, repo = self.repo.repo, secret_name = secret_name.as_ref() ); let resp = self.repo.crab._delete(route, None::<&()>).await?; crate::map_github_error(resp).await?; Ok(()) } } octocrab-0.31.2/src/api/repos/stargazers.rs000064400000000000000000000026021046102023000167260ustar 00000000000000use super::*; #[derive(serde::Serialize)] pub struct ListStarGazersBuilder<'octo, 'r> { #[serde(skip)] handler: &'r RepoHandler<'octo>, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo, 'r> ListStarGazersBuilder<'octo, 'r> { pub fn new(handler: &'r RepoHandler<'octo>) -> Self { Self { handler, per_page: None, page: None, } } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Sends the actual request. pub async fn send(self) -> crate::Result> { let route = format!( "/repos/{owner}/{repo}/stargazers", owner = self.handler.owner, repo = self.handler.repo ); let mut headers = http::header::HeaderMap::new(); headers.insert(ACCEPT, "application/vnd.github.star+json".parse().unwrap()); self.handler .crab .get_with_headers(route, Some(&self), Some(headers)) .await } } octocrab-0.31.2/src/api/repos/status.rs000064400000000000000000000070301046102023000160640ustar 00000000000000use super::*; use crate::models::{Status, StatusState}; #[derive(serde::Serialize)] pub struct CreateStatusBuilder<'octo, 'r> { #[serde(skip)] handler: &'r RepoHandler<'octo>, #[serde(skip)] pub sha: String, pub state: StatusState, #[serde(skip_serializing_if = "Option::is_none")] pub context: Option, #[serde(skip_serializing_if = "Option::is_none")] pub target_url: Option, #[serde(skip_serializing_if = "Option::is_none")] pub description: Option, } impl<'octo, 'r> CreateStatusBuilder<'octo, 'r> { pub(crate) fn new(handler: &'r RepoHandler<'octo>, sha: String, state: StatusState) -> Self { Self { handler, sha, state, context: None, target_url: None, description: None, } } /// The SHA hash of the target commit. pub fn sha(mut self, sha: String) -> Self { self.sha = sha; self } /// A string label to differentiate this status from the status of other systems. pub fn context(mut self, context: String) -> Self { self.context = Some(context); self } /// A short description of the status. pub fn description(mut self, description: String) -> Self { self.description = Some(description); self } /// The target URL to associate with this status. This URL will be linked from the GitHub UI to allow users to easily see the source of the status. /// For example, if your continuous integration system is posting build status, you would want to provide the deep link for the build output for this specific SHA: /// http://ci.example.com/user/repo/build/sha pub fn target(mut self, target: String) -> Self { self.target_url = Some(target); self } /// The state of the status. pub fn state(mut self, state: StatusState) -> Self { self.state = state; self } /// Sends the actual request. pub async fn send(self) -> Result { let route = format!( "/repos/{owner}/{repo}/statuses/{sha}", owner = self.handler.owner, repo = self.handler.repo, sha = self.sha ); self.handler.crab.post(route, Some(&self)).await } } #[derive(serde::Serialize)] pub struct ListStatusesBuilder<'octo, 'r> { #[serde(skip)] handler: &'r RepoHandler<'octo>, #[serde(skip)] sha: String, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo, 'r> ListStatusesBuilder<'octo, 'r> { pub fn new(handler: &'r RepoHandler<'octo>, sha: String) -> Self { Self { handler, sha, per_page: None, page: None, } } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Sends the actual request. pub async fn send(self) -> crate::Result> { let route = format!( "/repos/{owner}/{repo}/commits/{sha}/statuses", owner = self.handler.owner, repo = self.handler.repo, sha = self.sha, ); self.handler.crab.get(route, Some(&self)).await } } octocrab-0.31.2/src/api/repos/tags.rs000064400000000000000000000022351046102023000155010ustar 00000000000000use super::*; #[derive(serde::Serialize)] pub struct ListTagsBuilder<'octo, 'r> { #[serde(skip)] handler: &'r RepoHandler<'octo>, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo, 'r> ListTagsBuilder<'octo, 'r> { pub fn new(handler: &'r RepoHandler<'octo>) -> Self { Self { handler, per_page: None, page: None, } } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Sends the actual request. pub async fn send(self) -> crate::Result> { let route = format!( "/repos/{owner}/{repo}/tags", owner = self.handler.owner, repo = self.handler.repo ); self.handler.crab.get(route, Some(&self)).await } } octocrab-0.31.2/src/api/repos/teams.rs000064400000000000000000000022411046102023000156510ustar 00000000000000use super::*; #[derive(serde::Serialize)] pub struct ListTeamsBuilder<'octo, 'r> { #[serde(skip)] handler: &'r RepoHandler<'octo>, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo, 'r> ListTeamsBuilder<'octo, 'r> { pub fn new(handler: &'r RepoHandler<'octo>) -> Self { Self { handler, per_page: None, page: None, } } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Sends the actual request. pub async fn send(self) -> crate::Result> { let route = format!( "/repos/{owner}/{repo}/teams", owner = self.handler.owner, repo = self.handler.repo ); self.handler.crab.get(route, Some(&self)).await } } octocrab-0.31.2/src/api/repos.rs000064400000000000000000000461211046102023000145450ustar 00000000000000//! The repositories API. use http::header::ACCEPT; use http::request::Builder; use http::Uri; use snafu::ResultExt; mod branches; mod collaborators; mod commits; pub mod events; mod file; pub mod forks; mod generate; mod merges; mod pulls; pub mod releases; mod secrets; mod stargazers; mod status; mod tags; mod teams; use crate::error::HttpSnafu; use crate::repos::file::GetReadmeBuilder; use crate::{models, params, Octocrab, Result}; pub use branches::ListBranchesBuilder; pub use collaborators::ListCollaboratorsBuilder; pub use commits::ListCommitsBuilder; pub use file::{DeleteFileBuilder, GetContentBuilder, UpdateFileBuilder}; pub use generate::GenerateRepositoryBuilder; pub use merges::MergeBranchBuilder; pub use pulls::ListPullsBuilder; pub use releases::ReleasesHandler; pub use secrets::RepoSecretsHandler; pub use stargazers::ListStarGazersBuilder; pub use status::{CreateStatusBuilder, ListStatusesBuilder}; pub use tags::ListTagsBuilder; pub use teams::ListTeamsBuilder; /// Handler for GitHub's repository API. /// /// Created with [`Octocrab::repos`]. pub struct RepoHandler<'octo> { crab: &'octo Octocrab, owner: String, repo: String, } impl<'octo> RepoHandler<'octo> { pub(crate) fn new(crab: &'octo Octocrab, owner: String, repo: String) -> Self { Self { crab, owner, repo } } /// Get's a repository's license. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let license = octocrab::instance().repos("owner", "repo").license().await?; /// # Ok(()) /// # } /// ``` pub async fn license(&self) -> Result { let route = format!( "/repos/{owner}/{repo}/license", owner = self.owner, repo = self.repo, ); self.crab.get(route, None::<&()>).await } /// Get's a repository's public key. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let public_key = octocrab::instance().repos("owner", "repo").public_key().await?; /// # Ok(()) /// # } /// ``` pub async fn public_key(&self) -> Result { let route = format!( "/repos/{owner}/{repo}/actions/secrets/public-key", owner = self.owner, repo = self.repo, ); self.crab.get(route, None::<&()>).await } /// Fetches a single repository. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let repo = octocrab::instance() /// .repos("owner", "repo") /// .get() /// .await?; /// # Ok(()) /// # } /// ``` pub async fn get(&self) -> Result { let route = format!( "/repos/{owner}/{repo}", owner = self.owner, repo = self.repo, ); self.crab.get(route, None::<&()>).await } /// Fetches a repository's metrics. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let repo = octocrab::instance() /// .repos("owner", "repo") /// .get_community_profile_metrics() /// .await?; /// # Ok(()) /// # } /// ``` pub async fn get_community_profile_metrics(&self) -> Result { let route = format!( "/repos/{owner}/{repo}/community/profile", owner = self.owner, repo = self.repo, ); self.crab.get(route, None::<&()>).await } /// Fetches a single reference in the Git database. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// use octocrab::params::repos::Reference; /// /// let master = octocrab::instance() /// .repos("owner", "repo") /// .get_ref(&Reference::Branch("master".to_string())) /// .await?; /// # Ok(()) /// # } /// ``` pub async fn get_ref( &self, reference: ¶ms::repos::Reference, ) -> Result { let route = format!( "/repos/{owner}/{repo}/git/ref/{reference}", owner = self.owner, repo = self.repo, reference = reference.ref_url(), ); self.crab.get(route, None::<&()>).await } /// Fetches information about a git tag with the given `tag_sha`. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// use octocrab::params::repos::Reference; /// /// let master = octocrab::instance() /// .repos("owner", "repo") /// .get_tag("402b2026a41b26b691c429ddb0b9c27a31b27a6b") /// .await?; /// # Ok(()) /// # } /// ``` pub async fn get_tag(&self, tag_sha: impl Into) -> Result { let route = format!( "/repos/{owner}/{repo}/git/tags/{tag_sha}", owner = self.owner, repo = self.repo, tag_sha = tag_sha.into(), ); self.crab.get(route, None::<&()>).await } /// Creates a new reference for the repository. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let master_sha = ""; /// use octocrab::params::repos::Reference; /// /// // Given the SHA of the master branch, creates a 1.0 tag (🎉) /// octocrab::instance() /// .repos("owner", "repo") /// .create_ref(&Reference::Tag("1.0".to_string()), master_sha) /// .await?; /// # Ok(()) /// # } /// ``` pub async fn create_ref( &self, reference: ¶ms::repos::Reference, sha: impl Into, ) -> Result { let route = format!( "/repos/{owner}/{repo}/git/refs", owner = self.owner, repo = self.repo, ); self.crab .post( route, Some(&serde_json::json!({ "ref": reference.full_ref_url(), "sha": sha.into(), })), ) .await } /// Get repository content. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// /// octocrab::instance() /// .repos("owner", "repo") /// .get_content() /// .path("path/to/file") /// .r#ref("main") /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn get_content(&self) -> GetContentBuilder<'_, '_> { GetContentBuilder::new(self) } /// Get repository readme. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// /// octocrab::instance() /// .repos("owner", "repo") /// .get_readme() /// .path("path/to/file") /// .r#ref("main") /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn get_readme(&self) -> GetReadmeBuilder<'_, '_> { GetReadmeBuilder::new(self) } /// Creates a new file in the repository. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// use octocrab::models::repos::CommitAuthor; /// /// // Commit to add "crabs/ferris.txt" /// octocrab::instance() /// .repos("owner", "repo") /// .create_file( /// "crabs/ferris.txt", /// "Created ferris.txt", /// "Thought there’d never be a Rust Rap?\n" /// ) /// .branch("master") /// .commiter(CommitAuthor { /// name: "Octocat".to_string(), /// email: "octocat@github.com".to_string(), /// }) /// .author(CommitAuthor { /// name: "Ferris".to_string(), /// email: "ferris@rust-lang.org".to_string(), /// }) /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn create_file( &self, path: impl Into, message: impl Into, content: impl AsRef<[u8]>, ) -> UpdateFileBuilder<'_, '_> { use base64::Engine; UpdateFileBuilder::new( self, path.into(), message.into(), base64::prelude::BASE64_STANDARD.encode(content), None, ) } /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let blob_sha = ""; /// use octocrab::models::repos::CommitAuthor; /// /// // Given the file blob for "crabs/ferris.txt", commit to update the file. /// octocrab::instance() /// .repos("owner", "repo") /// .update_file( /// "crabs/ferris.txt", /// "Updated ferris.txt", /// "But me and Ferris Crab: best friends to the end.\n", /// blob_sha /// ) /// .branch("master") /// .commiter(CommitAuthor { /// name: "Octocat".to_string(), /// email: "octocat@github.com".to_string(), /// }) /// .author(CommitAuthor { /// name: "Ferris".to_string(), /// email: "ferris@rust-lang.org".to_string(), /// }) /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn update_file( &self, path: impl Into, message: impl Into, content: impl AsRef<[u8]>, sha: impl Into, ) -> UpdateFileBuilder<'_, '_> { use base64::Engine; UpdateFileBuilder::new( self, path.into(), message.into(), base64::prelude::BASE64_STANDARD.encode(content), Some(sha.into()), ) } /// Deletes a file in the repository. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let blob_sha = ""; /// use octocrab::models::repos::CommitAuthor; /// /// // Commit to delete "crabs/ferris.txt" /// octocrab::instance() /// .repos("owner", "repo") /// .delete_file( /// "crabs/ferris.txt", /// "Deleted ferris.txt", /// blob_sha /// ) /// .branch("master") /// .commiter(CommitAuthor { /// name: "Octocat".to_string(), /// email: "octocat@github.com".to_string(), /// }) /// .author(CommitAuthor { /// name: "Ferris".to_string(), /// email: "ferris@rust-lang.org".to_string(), /// }) /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn delete_file( &self, path: impl Into, message: impl Into, sha: impl Into, ) -> DeleteFileBuilder<'_, '_> { DeleteFileBuilder::new(self, path.into(), message.into(), sha.into()) } /// List tags from a repository. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let tags = octocrab::instance().repos("owner", "repo").list_tags().send().await?; /// # Ok(()) /// # } /// ``` pub fn list_tags(&self) -> ListTagsBuilder<'_, '_> { ListTagsBuilder::new(self) } /// List branches from a repository. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let branches = octocrab::instance() /// .repos("owner", "repo") /// .list_branches() /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn list_branches(&self) -> ListBranchesBuilder<'_, '_> { ListBranchesBuilder::new(self) } /// List commits from a repository /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let commits = octocrab::instance().repos("owner", "repo").list_commits().send().await?; /// # Ok(()) /// # } /// ``` pub fn list_commits(&self) -> ListCommitsBuilder<'_, '_> { ListCommitsBuilder::new(self) } /// List teams from a repository. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let teams = octocrab::instance().repos("owner", "repo").list_teams().send().await?; /// # Ok(()) /// # } /// ``` pub fn list_teams(&self) -> ListTeamsBuilder<'_, '_> { ListTeamsBuilder::new(self) } /// List collaborators from a repository. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let collaborators = octocrab::instance().repos("owner", "repo").list_collaborators().send().await?; /// # Ok(()) /// # } /// ``` pub fn list_collaborators(&self) -> ListCollaboratorsBuilder<'_, '_> { ListCollaboratorsBuilder::new(self) } /// List star_gazers from a repository. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let stargazers = octocrab::instance().repos("owner", "repo").list_stargazers().send().await?; /// # Ok(()) /// # } /// ``` pub fn list_stargazers(&self) -> ListStarGazersBuilder<'_, '_> { ListStarGazersBuilder::new(self) } /// Creates a `ReleasesHandler` for the specified repository. pub fn releases(&self) -> releases::ReleasesHandler<'_, '_> { releases::ReleasesHandler::new(self) } /// Create a status for a specified commit in the specified repository. pub fn create_status(&self, sha: String, state: models::StatusState) -> CreateStatusBuilder { CreateStatusBuilder::new(self, sha, state) } /// List statuses for a reference. pub fn list_statuses(&self, sha: String) -> ListStatusesBuilder<'_, '_> { ListStatusesBuilder::new(self, sha) } /// List pull requests for a reference. pub fn list_pulls(&self, sha: String) -> ListPullsBuilder<'_, '_> { ListPullsBuilder::new(self, sha) } /// List events on this repository. /// /// Takes an optional etag which allows for efficient polling. Here is a quick example to poll a /// repositories events. /// ```no_run /// # use std::convert::TryFrom; /// # use octocrab::{models::events::Event, etag::{Etagged,EntityTag}, Page}; /// # async fn run() -> octocrab::Result<()> { /// let mut etag = None; /// loop { /// let response: Etagged> = octocrab::instance() /// .repos("owner", "repo") /// .events() /// .etag(etag) /// .send() /// .await?; /// if let Some(page) = response.value { /// // do something with the page ... /// } else { /// println!("No new data received, trying again soon"); /// } /// etag = response.etag; /// // add a delay before the next iteration /// } /// # Ok(()) /// # } /// ``` pub fn events(&self) -> events::ListRepoEventsBuilder<'_, '_> { events::ListRepoEventsBuilder::new(self) } /// Gets the combined status for the specified reference. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// use octocrab::params::repos::Reference; /// /// let master = octocrab::instance() /// .repos("owner", "repo") /// .combined_status_for_ref(&Reference::Branch("main".to_string())) /// .await?; /// # Ok(()) /// # } /// ``` pub async fn combined_status_for_ref( &self, reference: ¶ms::repos::Reference, ) -> Result { let route = format!( "/repos/{owner}/{repo}/commits/{reference}/status", owner = self.owner, repo = self.repo, reference = reference.ref_url(), ); self.crab.get(route, None::<&()>).await } /// Creates a new repository from repository if it is a template. /// ```no_run /// # use http::Response; /// async fn run() -> octocrab::Result<()> { /// octocrab::instance() /// .repos("owner", "repo") /// .generate("rust") /// .owner("new_owner") /// .description("Description") /// .include_all_branches(true) /// .private(true) /// .send() /// .await /// # } /// ``` pub fn generate(&self, name: &str) -> GenerateRepositoryBuilder<'_, '_> { GenerateRepositoryBuilder::new(self, name) } /// Retrieve the contents of a file in raw format pub async fn raw_file( self, reference: impl Into, path: impl AsRef, ) -> Result> { let route = format!( "/repos/{owner}/{repo}/contents/{path}", owner = self.owner, repo = self.repo, path = path.as_ref(), ); let uri = self .crab .parameterized_uri(route, Some(&[("ref", &reference.into().0)]))?; let request = Builder::new() .uri(uri) .method(http::Method::GET) .header(ACCEPT, "application/vnd.github.v3.raw"); self.crab .execute(self.crab.build_request(request, None::<&()>)?) .await } /// Deletes this repository. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// octocrab::instance().repos("owner", "repo").delete().await /// # } /// ``` pub async fn delete(self) -> Result<()> { let route = format!( "/repos/{owner}/{repo}", owner = self.owner, repo = self.repo ); let uri = Uri::builder() .path_and_query(route) .build() .context(HttpSnafu)?; crate::map_github_error(self.crab._delete(uri, None::<&()>).await?) .await .map(drop) } /// Stream the repository contents as a .tar.gz pub async fn download_tarball( &self, reference: impl Into, ) -> Result> { let route = format!( "/repos/{owner}/{repo}/tarball/{reference}", owner = self.owner, repo = self.repo, reference = reference.into(), ); let uri = Uri::builder() .path_and_query(route) .build() .context(HttpSnafu)?; self.crab .follow_location_to_data(self.crab._get(uri).await?) .await } /// Check if a user is a repository collaborator pub async fn is_collaborator(&self, username: impl AsRef) -> Result { let route = format!( "/repos/{owner}/{repo}/collaborators/{username}", owner = self.owner, repo = self.repo, username = username.as_ref(), ); let uri = Uri::builder() .path_and_query(route) .build() .context(HttpSnafu)?; let response = self.crab._get(uri).await?; Ok(response.status().is_success()) } /// Merges `head` into the `base` branch. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// /// // Merges a feature branch into the master branch. /// octocrab::instance() /// .repos("owner", "repo") /// .merge("feature", "master") /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn merge( &self, head: impl Into, base: impl Into, ) -> MergeBranchBuilder<'octo, '_> { MergeBranchBuilder::new(self, head, base) } /// Handle secrets on the repository pub fn secrets(&self) -> RepoSecretsHandler<'_> { RepoSecretsHandler::new(self) } } octocrab-0.31.2/src/api/search.rs000064400000000000000000000126311046102023000146610ustar 00000000000000//! Using GitHub's search. use crate::{models, Octocrab}; /// Handler for the search API. /// /// Created with [`Octocrab::search`]. pub struct SearchHandler<'octo> { crab: &'octo Octocrab, } impl<'octo> SearchHandler<'octo> { pub(crate) fn new(crab: &'octo Octocrab) -> Self { Self { crab } } /// Searches for all the repositories matching the search query. /// ```no_run ///# async fn run() -> octocrab::Result<()> { /// let page = octocrab::instance() /// .search() /// .repositories("tetris language:rust") /// .sort("stars") /// .order("desc") /// .send() /// .await?; ///# Ok(()) ///# } /// ``` pub fn repositories<'query>( self, query: &'query (impl AsRef + ?Sized), ) -> QueryHandler<'octo, 'query, models::Repository> { QueryHandler::new(self.crab, "repositories", query.as_ref()) } /// Searches for all the commits matching the search query. /// ```no_run ///# async fn run() -> octocrab::Result<()> { /// let page = octocrab::instance() /// .search() /// .commits("hello world repo:XAMPPRocky/octocrab") /// .sort("author-date") /// .order("desc") /// .send() /// .await?; ///# Ok(()) ///# } /// ``` pub fn commits<'query>( self, query: &'query (impl AsRef + ?Sized), ) -> QueryHandler<'octo, 'query, models::repos::Commit> { QueryHandler::new(self.crab, "commits", query.as_ref()) } /// Searches for all users matching the search query. /// ```no_run ///# async fn run() -> octocrab::Result<()> { /// let page = octocrab::instance() /// .search() /// .users("bors type:user") /// .sort("followers") /// .order("desc") /// .send() /// .await?; ///# Ok(()) ///# } /// ``` pub fn users<'query>( self, query: &'query (impl AsRef + ?Sized), ) -> QueryHandler<'octo, 'query, models::Author> { QueryHandler::new(self.crab, "users", query.as_ref()) } /// Searches for all the issues matching the search query. /// ```no_run ///# async fn run() -> octocrab::Result<()> { /// let page = octocrab::instance() /// .search() /// .issues_and_pull_requests("GitHub Octocrab in:readme user:ferris") /// .sort("comments") /// .order("asc") /// .send() /// .await?; ///# Ok(()) ///# } /// ``` pub fn issues_and_pull_requests<'query>( self, query: &'query (impl AsRef + ?Sized), ) -> QueryHandler<'octo, 'query, models::issues::Issue> { QueryHandler::new(self.crab, "issues", query.as_ref()) } /// Searches for all code matching the search query. /// ```no_run ///# async fn run() -> octocrab::Result<()> { /// let page = octocrab::instance() /// .search() /// .code("println! language:rust repo:rust-lang/rust") /// .sort("indexed") /// .order("asc") /// .send() /// .await?; ///# Ok(()) ///# } /// ``` pub fn code<'query>( self, query: &'query (impl AsRef + ?Sized), ) -> QueryHandler<'octo, 'query, models::Code> { QueryHandler::new(self.crab, "code", query.as_ref()) } } /// A handler for handling search queries to GitHub. #[derive(Clone, Debug, serde::Serialize)] pub struct QueryHandler<'octo, 'query, T> { #[serde(skip)] return_type: std::marker::PhantomData, #[serde(skip)] crab: &'octo Octocrab, #[serde(skip)] route: &'static str, #[serde(rename = "q")] query: &'query str, per_page: Option, page: Option, #[serde(skip_serializing_if = "Option::is_none")] sort: Option, #[serde(skip_serializing_if = "Option::is_none")] order: Option, } impl<'octo, 'query, T> QueryHandler<'octo, 'query, T> { pub(crate) fn new(crab: &'octo Octocrab, route: &'static str, query: &'query str) -> Self { Self { crab, order: None, page: None, per_page: None, query, return_type: std::marker::PhantomData, route, sort: None, } } /// Sets the `sort` parameter for the query. The exact parameters for this /// method will vary based on what is being searched. pub fn sort>(mut self, sort: impl Into>) -> Self { self.sort = sort.into().map(S::into); self } /// Sets the `order` parameter for the query. The exact parameters for this /// method will vary based on what is being searched. pub fn order>(mut self, order: impl Into>) -> Self { self.order = order.into().map(S::into); self } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } } impl<'octo, 'query, T: serde::de::DeserializeOwned> QueryHandler<'octo, 'query, T> { /// Send the actual request. pub async fn send(self) -> crate::Result> { self.crab .get(&format!("/search/{}", self.route), Some(&self)) .await } } octocrab-0.31.2/src/api/teams/children.rs000064400000000000000000000023761046102023000163220ustar 00000000000000use super::*; use crate::{models, Page, Result}; #[derive(serde::Serialize)] pub struct ListChildTeamsBuilder<'octo, 'r> { #[serde(skip)] handler: &'r TeamHandler<'octo>, #[serde(skip)] slug: String, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo, 'r> ListChildTeamsBuilder<'octo, 'r> { pub(crate) fn new(handler: &'r TeamHandler<'octo>, slug: String) -> Self { Self { handler, slug, per_page: None, page: None, } } /// Results per page. pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Sends the actual request. pub async fn send(self) -> Result> { let route = format!( "/orgs/{org}/teams/{team}/teams", org = self.handler.owner, team = self.slug, ); self.handler.crab.get(route, Some(&self)).await } } octocrab-0.31.2/src/api/teams/create.rs000064400000000000000000000050431046102023000157670ustar 00000000000000use super::*; use crate::models::TeamId; use crate::params; #[derive(serde::Serialize)] pub struct CreateTeamBuilder<'octo, 'h, 'a, 'b> { #[serde(skip)] handler: &'h TeamHandler<'octo>, name: String, #[serde(skip_serializing_if = "Option::is_none")] description: Option, #[serde(skip_serializing_if = "Option::is_none")] maintainers: Option<&'a [String]>, #[serde(skip_serializing_if = "Option::is_none")] repo_names: Option<&'b [String]>, #[serde(skip_serializing_if = "Option::is_none")] privacy: Option, #[serde(skip_serializing_if = "Option::is_none")] permission: Option, #[serde(skip_serializing_if = "Option::is_none")] parent_team_id: Option, } impl<'octo, 'h, 'a, 'b> CreateTeamBuilder<'octo, 'h, 'a, 'b> { pub(crate) fn new(handler: &'h TeamHandler<'octo>, name: String) -> Self { Self { handler, name, description: None, maintainers: None, repo_names: None, privacy: None, permission: None, parent_team_id: None, } } /// The description of the team. pub fn description(mut self, description: impl Into) -> Self { self.description = Some(description.into()); self } /// The organization members who will become team maintainers. pub fn maintainers(mut self, maintainers: &'a (impl AsRef<[String]> + ?Sized)) -> Self { self.maintainers = Some(maintainers.as_ref()); self } /// The repositories to add the team to. /// /// Note: the repo name must be its full name, e.g. `"org/repo"`. pub fn repo_names(mut self, repo_names: &'b (impl AsRef<[String]> + ?Sized)) -> Self { self.repo_names = Some(repo_names.as_ref()); self } /// The level of privacy this team should have. /// /// For parents or child teams, only `Privacy::Closed` is valid. pub fn privacy(mut self, privacy: impl Into) -> Self { self.privacy = Some(privacy.into()); self } /// The ID of the team to set as the parent team. pub fn parent_team_id(mut self, parent_team_id: TeamId) -> Self { self.parent_team_id = Some(parent_team_id); self } /// Sends the actual request. pub async fn send(self) -> Result { let route = format!("/orgs/{org}/teams", org = self.handler.owner,); self.handler.crab.post(route, Some(&self)).await } } octocrab-0.31.2/src/api/teams/edit.rs000064400000000000000000000036241046102023000154540ustar 00000000000000use super::*; use crate::models::TeamId; use crate::params; #[derive(serde::Serialize)] pub struct EditTeamBuilder<'octo, 'r> { #[serde(skip)] handler: &'r TeamHandler<'octo>, #[serde(skip)] slug: String, name: String, #[serde(skip_serializing_if = "Option::is_none")] description: Option, #[serde(skip_serializing_if = "Option::is_none")] privacy: Option, #[serde(skip_serializing_if = "Option::is_none")] permission: Option, #[serde(skip_serializing_if = "Option::is_none")] parent_team_id: Option, } impl<'octo, 'r> EditTeamBuilder<'octo, 'r> { pub(crate) fn new(handler: &'r TeamHandler<'octo>, slug: String, name: String) -> Self { Self { handler, slug, name, description: None, privacy: None, permission: None, parent_team_id: None, } } /// The description of the team. pub fn description(mut self, description: impl Into) -> Self { self.description = Some(description.into()); self } /// The level of privacy this team should have. /// /// For parents or child teams, only `Privacy::Closed` is valid. pub fn privacy(mut self, privacy: impl Into) -> Self { self.privacy = Some(privacy.into()); self } /// The ID of the team to set as the parent team. pub fn parent_team_id(mut self, parent_team_id: TeamId) -> Self { self.parent_team_id = Some(parent_team_id); self } /// Sends the actual request. pub async fn send(self) -> Result { let route = format!( "/orgs/{org}/teams/{team}", org = self.handler.owner, team = self.slug, ); self.handler.crab.patch(route, Some(&self)).await } } octocrab-0.31.2/src/api/teams/invitations.rs000064400000000000000000000024161046102023000170740ustar 00000000000000use super::*; use crate::{models::teams, Page, Result}; #[derive(serde::Serialize)] pub struct ListTeamInvitationsBuilder<'octo, 'r> { #[serde(skip)] handler: &'r TeamHandler<'octo>, #[serde(skip)] slug: String, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo, 'r> ListTeamInvitationsBuilder<'octo, 'r> { pub(crate) fn new(handler: &'r TeamHandler<'octo>, slug: String) -> Self { Self { handler, slug, per_page: None, page: None, } } /// Results per page. pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Sends the actual request. pub async fn send(self) -> Result> { let route = format!( "/orgs/{org}/teams/{team}/invitations", org = self.handler.owner, team = self.slug, ); self.handler.crab.get(route, Some(&self)).await } } octocrab-0.31.2/src/api/teams/list.rs000064400000000000000000000021451046102023000154770ustar 00000000000000use super::*; use crate::{models, Page, Result}; #[derive(serde::Serialize)] pub struct ListTeamsBuilder<'octo, 'r> { #[serde(skip)] handler: &'r TeamHandler<'octo>, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo, 'r> ListTeamsBuilder<'octo, 'r> { pub(crate) fn new(handler: &'r TeamHandler<'octo>) -> Self { Self { handler, per_page: None, page: None, } } /// Results per page. pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Sends the actual request. pub async fn send(self) -> Result> { let route = format!("/orgs/{owner}/teams", owner = self.handler.owner); self.handler.crab.get(route, Some(&self)).await } } octocrab-0.31.2/src/api/teams/members.rs000064400000000000000000000023641046102023000161610ustar 00000000000000use super::*; use crate::{models, Page, Result}; #[derive(serde::Serialize)] pub struct ListTeamMembersBuilder<'octo, 'r> { #[serde(skip)] handler: &'r TeamHandler<'octo>, #[serde(skip)] slug: String, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo, 'r> ListTeamMembersBuilder<'octo, 'r> { pub(crate) fn new(handler: &'r TeamHandler<'octo>, slug: String) -> Self { Self { handler, slug, per_page: None, page: None, } } /// Results per page. pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Sends the actual request. pub async fn send(self) -> Result> { let route = format!( "/orgs/{org}/teams/{team}/members", org = self.handler.owner, team = self.slug, ); self.handler.crab.get(route, Some(&self)).await } } octocrab-0.31.2/src/api/teams/team_repos.rs000064400000000000000000000077421046102023000166720ustar 00000000000000use crate::error::HttpSnafu; use crate::params; use crate::{models, FromResponse, Octocrab, Result}; use http::header::ACCEPT; use http::request::Builder; use http::{StatusCode, Uri}; use snafu::ResultExt; #[derive(Debug, serde::Serialize)] struct PermissionUpdateBody { permission: params::teams::Permission, } /// Handler for managing a team's repositories through /// GitHub's teams API. /// /// Created with [`TeamHandler::repos`] /// /// [`TeamHandler::repos`]: ./struct.TeamHandler.html#method.repos pub struct TeamRepoHandler<'octo> { crab: &'octo Octocrab, org: String, team: String, } impl<'octo> TeamRepoHandler<'octo> { pub(crate) fn new(crab: &'octo Octocrab, org: String, team: String) -> Self { Self { crab, org, team } } /// Checks if a team manages a repository, returning the repository if it does. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let manages_repo = octocrab::instance() /// .teams("owner") /// .repos("team") /// .check_manages("owner", "repo") /// .await /// .is_ok(); /// # Ok(()) /// # } /// ``` pub async fn check_manages( &self, repo_owner: impl Into, repo_name: impl Into, ) -> Result> { let route = format!( "/orgs/{org}/teams/{team}/repos/{owner}/{repo}", org = self.org, team = self.team, owner = repo_owner.into(), repo = repo_name.into(), ); let uri = Uri::builder() .path_and_query(route) .build() .context(HttpSnafu)?; let request = Builder::new() .method("GET") .uri(uri) .header(ACCEPT, crate::format_media_type("inertia-preview+json")); let request = self.crab.build_request(request, None::<&()>)?; let res = self.crab.execute(request).await?; if res.status() == StatusCode::NOT_FOUND { return Ok(None); } Ok(Some(models::Repository::from_response(res).await?)) } /// Updates a team's permissions for a repository. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// use octocrab::params; /// /// octocrab::instance() /// .teams("owner") /// .repos("team") /// .add_or_update("owner", "repo", params::teams::Permission::Maintain) /// .await?; /// # Ok(()) /// # } /// ``` pub async fn add_or_update( &self, repo_owner: impl Into, repo_name: impl Into, permission: impl Into>, ) -> Result<()> { let route = format!( "/orgs/{org}/teams/{team}/repos/{owner}/{repo}", org = self.org, team = self.team, owner = repo_owner.into(), repo = repo_name.into(), ); let perm_body = permission .into() .map(|p| PermissionUpdateBody { permission: p }); let uri = Uri::builder() .path_and_query(route) .build() .context(HttpSnafu)?; crate::map_github_error(self.crab._put(uri, perm_body.as_ref()).await?) .await .map(drop) } /// Removes a repository from a team. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// octocrab::instance() /// .teams("owner") /// .repos("team") /// .remove("owner", "repo") /// .await?; /// # Ok(()) /// # } /// ``` pub async fn remove( &self, repo_owner: impl Into, repo_name: impl Into, ) -> Result<()> { let route = format!( "/orgs/{org}/teams/{team}/repos/{owner}/{repo}", org = self.org, team = self.team, owner = repo_owner.into(), repo = repo_name.into(), ); self.crab.delete(route, None::<&()>).await } } octocrab-0.31.2/src/api/teams.rs000064400000000000000000000132661046102023000145320ustar 00000000000000//! The Teams API mod children; mod create; mod edit; mod invitations; mod list; mod members; mod team_repos; pub use self::{ children::ListChildTeamsBuilder, create::CreateTeamBuilder, edit::EditTeamBuilder, invitations::ListTeamInvitationsBuilder, list::ListTeamsBuilder, members::ListTeamMembersBuilder, team_repos::TeamRepoHandler, }; use http::Uri; use snafu::ResultExt; use crate::error::HttpSnafu; use crate::{models, Octocrab, Result}; /// Handler for GitHub's teams API. /// /// Created with [`Octocrab::teams`]. pub struct TeamHandler<'octo> { crab: &'octo Octocrab, owner: String, } impl<'octo> TeamHandler<'octo> { pub(crate) fn new(crab: &'octo Octocrab, owner: String) -> Self { Self { crab, owner } } /// Lists teams in the organization. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let teams = octocrab::instance() /// .teams("owner") /// .list() /// .per_page(10) /// .page(1u8) /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn list(&self) -> ListTeamsBuilder<'_, '_> { ListTeamsBuilder::new(self) } /// Gets a team from its slug. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let team = octocrab::instance() /// .teams("owner") /// .get("team") /// .await?; /// # Ok(()) /// # } /// ``` pub async fn get(&self, team_slug: impl Into) -> Result { let route = format!( "/orgs/{org}/teams/{team}", org = self.owner, team = team_slug.into(), ); self.crab.get(route, None::<&()>).await } /// Creates a new team in the organization. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// use octocrab::params; /// /// octocrab::instance() /// .teams("owner") /// .create("new-team") /// .description("My team created from Octocrab!") /// .maintainers(&vec![String::from("ferris")]) /// .repo_names(&vec![String::from("crab-stuff")]) /// .privacy(params::teams::Privacy::Closed) /// .parent_team_id(1u64.into()) /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn create(&self, name: impl Into) -> CreateTeamBuilder { CreateTeamBuilder::new(self, name.into()) } /// Creates a new team in the organization. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// use octocrab::params; /// /// octocrab::instance() /// .teams("owner") /// .edit("some-team", "Some Team") /// .description("I edited from Octocrab!") /// .privacy(params::teams::Privacy::Secret) /// .parent_team_id(2u64.into()) /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn edit(&self, team_slug: impl Into, name: impl Into) -> EditTeamBuilder { EditTeamBuilder::new(self, team_slug.into(), name.into()) } /// Deletes a team from the organization. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// octocrab::instance().teams("owner").delete("some-team").await?; /// # Ok(()) /// # } /// ``` pub async fn delete(&self, team_slug: impl Into) -> Result<()> { let route = format!( "/orgs/{org}/teams/{team}", org = self.owner, team = team_slug.into(), ); let uri = Uri::builder() .path_and_query(route) .build() .context(HttpSnafu)?; crate::map_github_error(self.crab._delete(uri, None::<&()>).await?) .await .map(drop) } /// List the child teams of a team in the organization. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); /// octocrab::instance() /// .teams("owner") /// .list_children("parent-team") /// .per_page(5) /// .page(1u8) /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn list_children(&self, team_slug: impl Into) -> ListChildTeamsBuilder { ListChildTeamsBuilder::new(self, team_slug.into()) } /// Creates a new `TeamRepoHandler` for the specified team, /// that allows you to manage this team's repositories. pub fn repos(&self, team_slug: impl Into) -> TeamRepoHandler { TeamRepoHandler::new(self.crab, self.owner.clone(), team_slug.into()) } /// List the members of a team in the organization. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); /// octocrab::instance() /// .teams("owner") /// .members("team-name-here") /// .per_page(5) /// .page(1u8) /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn members(&self, team_slug: impl Into) -> ListTeamMembersBuilder { ListTeamMembersBuilder::new(self, team_slug.into()) } /// List the pending invitations for a team in an organization. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); /// octocrab::instance() /// .teams("owner") /// .invitations("team-name-here") /// .per_page(5) /// .page(1u8) /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn invitations(&self, team_slug: impl Into) -> ListTeamInvitationsBuilder { ListTeamInvitationsBuilder::new(self, team_slug.into()) } } octocrab-0.31.2/src/api/users/user_repos.rs000064400000000000000000000057161046102023000167510ustar 00000000000000use crate::api::users::UserHandler; use crate::Page; /// A builder pattern struct for listing a user's repositories. /// /// created by [`UserHandler::repos`] /// /// [`UserHandler::repos`]: ./struct.UserHandler.html#method.repos #[derive(serde::Serialize)] pub struct ListUserReposBuilder<'octo, 'b> { #[serde(skip)] handler: &'b UserHandler<'octo>, #[serde(skip_serializing_if = "Option::is_none")] r#type: Option, #[serde(skip_serializing_if = "Option::is_none")] sort: Option, #[serde(skip_serializing_if = "Option::is_none")] direction: Option, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo, 'b> ListUserReposBuilder<'octo, 'b> { pub(crate) fn new(handler: &'b UserHandler<'octo>) -> Self { Self { handler, r#type: None, sort: None, direction: None, per_page: None, page: None, } } /// Repository ownership type. pub fn r#type(mut self, r#type: impl Into) -> Self { self.r#type = Some(r#type.into()); self } /// What to sort results by. pub fn sort(mut self, sort: impl Into) -> Self { self.sort = Some(sort.into()); self } /// The direction of the sort. pub fn direction(mut self, direction: impl Into) -> Self { self.direction = Some(direction.into()); self } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Sends the actual request. pub async fn send(self) -> crate::Result> { let route = format!("/users/{user}/repos", user = self.handler.user); self.handler.crab.get(route, Some(&self)).await } } #[cfg(test)] mod tests { #[tokio::test] async fn serialize() { let octocrab = crate::Octocrab::default(); let handler = octocrab.users("foo"); let request = handler .repos() .r#type(crate::params::users::repos::Type::Member) .sort(crate::params::repos::Sort::Updated) .direction(crate::params::Direction::Ascending) .per_page(87) .page(3u8); assert_eq!( serde_json::to_value(request).unwrap(), serde_json::json!({ "type": "member", "sort": "updated", "direction": "asc", "per_page": 87, "page": 3, }) ) } } octocrab-0.31.2/src/api/users.rs000064400000000000000000000006561046102023000145610ustar 00000000000000//! The users API. mod user_repos; pub use self::user_repos::ListUserReposBuilder; use crate::Octocrab; pub struct UserHandler<'octo> { crab: &'octo Octocrab, user: String, } impl<'octo> UserHandler<'octo> { pub(crate) fn new(crab: &'octo Octocrab, user: String) -> Self { Self { crab, user } } pub fn repos(&self) -> ListUserReposBuilder<'_, '_> { ListUserReposBuilder::new(self) } } octocrab-0.31.2/src/api/workflows.rs000064400000000000000000000246251046102023000154570ustar 00000000000000use crate::models::RunId; use crate::{models, Octocrab, Page, Result}; pub struct WorkflowsHandler<'octo> { crab: &'octo Octocrab, owner: String, repo: String, } /// Handler for GitHub's workflows API for actions. /// /// Created with [`Octocrab::workflows`]. impl<'octo> WorkflowsHandler<'octo> { pub(crate) fn new(crab: &'octo Octocrab, owner: String, repo: String) -> Self { Self { crab, owner, repo } } /// List workflow definitions in the repository. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); /// /// let issue = octocrab.workflows("owner", "repo") /// .list() /// // Optional Parameters /// .per_page(100) /// .page(1u8) /// // Send the request /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn list(&self) -> ListWorkflowsBuilder<'_, '_> { ListWorkflowsBuilder::new(self) } pub async fn get(&self, run_id: RunId) -> Result { let route = format!( "/repos/{owner}/{repo}/actions/runs/{run_id}", owner = self.owner, repo = self.repo, run_id = run_id, ); self.crab.get(route, None::<&()>).await } /// List runs in the specified workflow. /// workflow_file_or_id can be either file name or numeric expression. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); /// /// let issue = octocrab.workflows("owner", "repo") /// .list_runs("ci.yml") /// // Optional Parameters /// .actor("octocat") /// .branch("master") /// .event("push") /// .status("success") /// .per_page(100) /// .page(1u8) /// // Send the request /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn list_runs(&self, workflow_file_or_id: impl Into) -> ListRunsBuilder<'_, '_> { ListRunsBuilder::new( self, ListRunsRequestType::ByWorkflow(workflow_file_or_id.into()), ) } /// List runs for the specified owner and repository. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// let octocrab = octocrab::Octocrab::default(); /// /// let runs = octocrab.workflows("owner", "repo") /// .list_all_runs() /// // Optional Parameters /// .actor("octocat") /// .branch("master") /// .event("pull_request") /// .status("success") /// .per_page(100) /// .page(1u8) /// // Send the request /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn list_all_runs(&self) -> ListRunsBuilder<'_, '_> { ListRunsBuilder::new(self, ListRunsRequestType::ByRepo) } /// List job results in the specified run. /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # let octocrab = octocrab::Octocrab::default(); /// use octocrab::params::workflows::Filter; /// /// let issue = octocrab.workflows("owner", "repo") /// .list_jobs(1234u64.into()) /// // Optional Parameters /// .per_page(100) /// .page(1u8) /// .filter(Filter::All) /// // Send the request /// .send() /// .await?; /// # Ok(()) /// # } /// ``` pub fn list_jobs(&self, run_id: RunId) -> ListJobsBuilder<'_, '_> { ListJobsBuilder::new(self, run_id) } } #[derive(serde::Serialize)] pub struct ListWorkflowsBuilder<'octo, 'b> { #[serde(skip)] handler: &'b WorkflowsHandler<'octo>, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo, 'b> ListWorkflowsBuilder<'octo, 'b> { pub(crate) fn new(handler: &'b WorkflowsHandler<'octo>) -> Self { Self { handler, per_page: None, page: None, } } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Sends the actual request. pub async fn send(self) -> Result> { let route = format!( "/repos/{owner}/{repo}/actions/workflows", owner = self.handler.owner, repo = self.handler.repo ); self.handler.crab.get(route, Some(&self)).await } } /// The type of list workflow runs request. pub(crate) enum ListRunsRequestType { ByRepo, ByWorkflow(String), } #[derive(serde::Serialize)] pub struct ListRunsBuilder<'octo, 'b> { #[serde(skip)] handler: &'b WorkflowsHandler<'octo>, #[serde(skip)] r#type: ListRunsRequestType, #[serde(skip_serializing_if = "Option::is_none")] actor: Option, #[serde(skip_serializing_if = "Option::is_none")] branch: Option, #[serde(skip_serializing_if = "Option::is_none")] event: Option, #[serde(skip_serializing_if = "Option::is_none")] status: Option, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, #[serde(skip_serializing_if = "Option::is_none")] exclude_pull_requests: Option, } impl<'octo, 'b> ListRunsBuilder<'octo, 'b> { pub(crate) fn new(handler: &'b WorkflowsHandler<'octo>, r#type: ListRunsRequestType) -> Self { Self { handler, r#type, actor: None, branch: None, event: None, status: None, per_page: None, page: None, exclude_pull_requests: None, } } /// Someone who runs workflows. Use the login to specify a user. pub fn actor(mut self, actor: impl Into) -> Self { self.actor = Some(actor.into()); self } /// A branch associated with workflows. Use the name of the branch of the push. pub fn branch(mut self, branch: impl Into) -> Self { self.branch = Some(branch.into()); self } /// An event associated with workflows. Can be e.g. push, pull_request, issue, /// ... and many variations. See official "Events that trigger workflows." doc. pub fn event(mut self, event: impl Into) -> Self { self.event = Some(event.into()); self } /// A status associated with workflows. /// status or conclusion can be specified. e.g. success, in_progress, waiting... pub fn status(mut self, status: impl Into) -> Self { self.status = Some(status.into()); self } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Whether to exclude the pull requests or not. pub fn exclude_pull_requests(mut self, exclude_pull_requests: impl Into) -> Self { self.exclude_pull_requests = Some(exclude_pull_requests.into()); self } /// Sends the actual request. pub async fn send(self) -> Result> { let route = match self.r#type { ListRunsRequestType::ByRepo => format!( "/repos/{owner}/{repo}/actions/runs", owner = self.handler.owner, repo = self.handler.repo ), ListRunsRequestType::ByWorkflow(ref workflow_id) => format!( "/repos/{owner}/{repo}/actions/workflows/{workflow_id}/runs", owner = self.handler.owner, repo = self.handler.repo, workflow_id = workflow_id ), }; self.handler.crab.get(route, Some(&self)).await } } #[derive(serde::Serialize)] pub struct ListJobsBuilder<'octo, 'b> { #[serde(skip)] handler: &'b WorkflowsHandler<'octo>, #[serde(skip)] run_id: RunId, #[serde(skip_serializing_if = "Option::is_none")] filter: Option, #[serde(skip_serializing_if = "Option::is_none")] per_page: Option, #[serde(skip_serializing_if = "Option::is_none")] page: Option, } impl<'octo, 'b> ListJobsBuilder<'octo, 'b> { pub(crate) fn new(handler: &'b WorkflowsHandler<'octo>, run_id: RunId) -> Self { Self { handler, run_id, per_page: None, page: None, filter: None, } } /// Results per page (max 100). pub fn per_page(mut self, per_page: impl Into) -> Self { self.per_page = Some(per_page.into()); self } /// Page number of the results to fetch. pub fn page(mut self, page: impl Into) -> Self { self.page = Some(page.into()); self } /// Filters jobs by their completed_at timestamp. Choose latest or all. pub fn filter(mut self, filter: impl Into) -> Self { self.filter = Some(filter.into()); self } /// Sends the actual request. pub async fn send(self) -> Result> { let route = format!( "/repos/{owner}/{repo}/actions/runs/{run_id}/jobs", owner = self.handler.owner, repo = self.handler.repo, run_id = self.run_id, ); self.handler.crab.get(route, Some(&self)).await } } #[cfg(test)] mod tests { #[tokio::test] async fn serialize() { use crate::params::workflows::Filter; let octocrab = crate::Octocrab::default(); let handler = octocrab.workflows("rust-lang", "rust"); let list_jobs = handler .list_jobs(1234u64.into()) .filter(Filter::All) .per_page(100) .page(1u8); assert_eq!( serde_json::to_value(list_jobs).unwrap(), serde_json::json!({ "filter": "all", "per_page": 100, "page": 1, }) ) } } octocrab-0.31.2/src/api.rs000064400000000000000000000005331046102023000134120ustar 00000000000000pub mod actions; pub mod activity; pub mod apps; pub mod checks; pub mod commits; pub mod current; pub mod events; pub mod gists; pub mod gitignore; pub mod issues; pub mod licenses; pub mod markdown; pub mod orgs; pub mod projects; pub mod pulls; pub mod ratelimit; pub mod repos; pub mod search; pub mod teams; pub mod users; pub mod workflows; octocrab-0.31.2/src/auth.rs000064400000000000000000000215101046102023000136000ustar 00000000000000//! Authentication related types and functions. use crate::models::AppId; use crate::Result; use either::Either; use jsonwebtoken::{Algorithm, EncodingKey, Header}; use secrecy::{ExposeSecret, SecretString}; use serde::{Deserialize, Serialize}; use std::fmt; use std::time::SystemTime; use snafu::*; /// The data necessary to authenticate as a Github App #[derive(Clone)] pub struct AppAuth { /// Github's app ID pub app_id: AppId, /// The app's RSA private key pub key: EncodingKey, } impl fmt::Debug for AppAuth { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("AppAuth") .field("app_id", &self.app_id) .finish_non_exhaustive() } } /// The forms of authentication we support pub enum Auth { /// No authentication None, // Basic HTTP authentication (username:password) Basic { /// Username username: String, /// Password password: String, }, /// Authenticate using a Github personal access token PersonalToken(SecretString), /// Authenticate as a Github App App(AppAuth), /// Authenticate as a Github OAuth App OAuth(OAuth), /// Authenticate using a User Access Token UserAccessToken(SecretString), } impl Default for Auth { fn default() -> Self { Self::None } } /// Create a JSON Web Token that can be used to authenticate an a GitHub application. /// /// See: https://docs.github.com/en/developers/apps/getting-started-with-apps/setting-up-your-development-environment-to-create-a-github-app#authenticating-as-a-github-app pub fn create_jwt( github_app_id: AppId, key: &EncodingKey, ) -> Result { #[derive(Serialize)] struct Claims { iss: AppId, iat: usize, exp: usize, } let now = SystemTime::UNIX_EPOCH.elapsed().unwrap().as_secs() as usize; // Github only allows JWTs that expire in the next 10 minutes. // The token is issued 60 seconds in the past and expires in 9 minutes, // to allow some clock drift. let claims = Claims { iss: github_app_id, iat: now - 60, exp: now + (9 * 60), }; let header = Header::new(Algorithm::RS256); jsonwebtoken::encode(&header, &claims, key) } impl AppAuth { /// Currently we don't cache these, but we could if we want to avoid /// an RSA signature operation per App-authorized API call. pub fn generate_bearer_token(&self) -> Result { create_jwt(self.app_id, &self.key).context(crate::error::JWTSnafu) } } /// The data necessary to authenticate as a github OAtuh app. #[derive(Clone, Deserialize)] #[serde(from = "OAuthWire")] pub struct OAuth { pub access_token: SecretString, pub token_type: String, pub scope: Vec, } /// The wire format of the OAuth struct. #[derive(Deserialize)] struct OAuthWire { access_token: String, token_type: String, scope: String, } impl From for OAuth { fn from(value: OAuthWire) -> Self { OAuth { access_token: SecretString::from(value.access_token), token_type: value.token_type, scope: value.scope.split(',').map(ToString::to_string).collect(), } } } impl crate::Octocrab { /// Authenticate with Github's device flow. This starts the process to obtain a new `OAuth`. /// /// See https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps#device-flow for details. /// /// Note: To authenticate against public Github, the `Octocrab` that calls this method /// *must* be constructed with `base_uri: "https://github.com"` and extra header /// "ACCEPT: application/json". For example: /// ```no_run /// # async fn run() -> octocrab::Result<()> { /// # use http::header::ACCEPT; /// let crab = octocrab::Octocrab::builder() /// .base_uri("https://github.com")? /// .add_header(ACCEPT, "application/json".to_string()) /// .build()?; /// # Ok(()) /// # } /// ``` pub async fn authenticate_as_device( &self, client_id: &SecretString, scope: I, ) -> Result where I: IntoIterator, S: AsRef, { let scope = { let mut scopes = scope.into_iter(); let first = scopes .next() .map(|s| s.as_ref().to_string()) .unwrap_or_default(); scopes.fold(first, |i: String, n| i + "," + n.as_ref()) }; let codes: DeviceCodes = self .post( "/login/device/code", Some(&DeviceFlow { client_id: client_id.expose_secret(), scope: &scope, }), ) .await?; Ok(codes) } } /// The device codes as returned from step 1 of Github's device flow. /// /// See https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps#response-parameters #[derive(Deserialize, Clone)] #[non_exhaustive] pub struct DeviceCodes { /// The device verification code is 40 characters and used to verify the device. pub device_code: String, /// The user verification code is displayed on the device so the user can enter the /// code in a browser. This code is 8 characters with a hyphen in the middle. pub user_code: String, /// The verification URL where users need to enter the user_code: https://github.com/login/device. pub verification_uri: String, /// The number of seconds before the device_code and user_code expire. The default is /// 900 seconds or 15 minutes. pub expires_in: u64, /// The minimum number of seconds that must pass before you can make a new access /// token request (POST https://github.com/login/oauth/access_token) to complete the /// device authorization. For example, if the interval is 5, then you cannot make a /// new request until 5 seconds pass. If you make more than one request over 5 /// seconds, then you will hit the rate limit and receive a slow_down error. pub interval: u64, } impl DeviceCodes { /// Poll Github to see if authentication codes are available. /// /// See `https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps#response-parameters` for details. pub async fn poll_once( &self, crab: &crate::Octocrab, client_id: &SecretString, ) -> Result> { let poll: TokenResponse = crab .post( "/login/oauth/access_token", Some(&PollForDevice { client_id: client_id.expose_secret(), device_code: &self.device_code, grant_type: "urn:ietf:params:oauth:grant-type:device_code", }), ) .await?; Ok(match poll { TokenResponse::Ok(k) => Either::Left(k), TokenResponse::Contine { error } => Either::Right(error), }) } } /// See https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps#input-parameters #[derive(Serialize)] struct DeviceFlow<'a> { client_id: &'a str, scope: &'a str, } #[derive(Deserialize)] #[serde(untagged)] enum TokenResponse { // We got the auth information. Ok(OAuth), // We got an error that allows us to continue polling. Contine { error: Continue }, } /// Control flow when polling the device flow authorization. #[derive(Deserialize, Debug, Clone, Copy)] #[serde(rename_all = "snake_case")] pub enum Continue { /// When you receive the slow_down error, 5 extra seconds are added to the minimum /// interval or timeframe required between your requests using POST /// https://github.com/login/oauth/access_token. For example, if the starting interval /// required at least 5 seconds between requests and you get a slow_down error response, /// you must now wait a minimum of 10 seconds before making a new request for an OAuth /// access token. The error response includes the new interval that you must use. SlowDown, /// This error occurs when the authorization request is pending and the user hasn't /// entered the user code yet. The app is expected to keep polling the POST /// https://github.com/login/oauth/access_token request without exceeding the /// interval, which requires a minimum number of seconds between each request. AuthorizationPending, } #[derive(Serialize)] struct PollForDevice<'a> { /// Required. The client ID you received from GitHub for your OAuth App. client_id: &'a str, /// Required. The device verification code you received from the POST https://github.com/login/device/code request. device_code: &'a str, /// Required. The grant type must be urn:ietf:params:oauth:grant-type:device_code. grant_type: &'static str, } octocrab-0.31.2/src/error.rs000064400000000000000000000062671046102023000140040ustar 00000000000000use http::uri::InvalidUri; use snafu::{Backtrace, Snafu}; use std::fmt; use std::fmt::{Display, Formatter}; use std::string::FromUtf8Error; use tower::BoxError; //This is workaround until I figure out how to get TryInto errors to work #[derive(Debug)] pub struct UriParseError; impl Display for UriParseError { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "Failed to parse URI") } } impl std::error::Error for UriParseError {} /// An error that could have occurred while using [`crate::Octocrab`]. #[derive(Snafu, Debug)] #[snafu(visibility(pub))] pub enum Error { GitHub { source: GitHubError, backtrace: Backtrace, }, UriParse { source: UriParseError, backtrace: Backtrace, }, Uri { source: InvalidUri, backtrace: Backtrace, }, InvalidHeaderValue { source: http::header::InvalidHeaderValue, backtrace: Backtrace, }, #[snafu(display("HTTP Error: {}\n\nFound at {}", source, backtrace))] Http { source: http::Error, backtrace: Backtrace, }, InvalidUtf8 { source: FromUtf8Error, backtrace: Backtrace, }, Encoder { source: std::io::Error, backtrace: Backtrace, }, #[snafu(display("Service Error: {}\n\nFound at {}", source, backtrace))] Service { source: BoxError, backtrace: Backtrace, }, #[snafu(display("Hyper Error: {}\n\nFound at {}", source, backtrace))] Hyper { source: hyper::Error, backtrace: Backtrace, }, #[snafu(display("Serde Url Encode Error: {}\nFound at {}", source, backtrace))] SerdeUrlEncoded { source: serde_urlencoded::ser::Error, backtrace: Backtrace, }, #[snafu(display("Serde Error: {}\nFound at {}", source, backtrace))] Serde { source: serde_json::Error, backtrace: Backtrace, }, #[snafu(display("JSON Error in {}: {}\nFound at {}", source.path(), source.inner(), backtrace))] Json { source: serde_path_to_error::Error, backtrace: Backtrace, }, #[snafu(display("JWT Error in {}\nFound at {}", source, backtrace))] JWT { source: jsonwebtoken::errors::Error, backtrace: Backtrace, }, Other { source: Box, backtrace: Backtrace, }, } /// An error returned from GitHub's API. #[derive(serde::Deserialize, Debug, Clone)] #[non_exhaustive] pub struct GitHubError { pub documentation_url: Option, pub errors: Option>, pub message: String, } impl fmt::Display for GitHubError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.message)?; if let Some(documentation_url) = &self.documentation_url { write!(f, "\nDocumentation URL: {documentation_url}")?; } if let Some(errors) = &self.errors.as_ref().filter(|errors| !errors.is_empty()) { write!(f, "\nErrors:")?; for error in errors.iter() { write!(f, "\n- {error}")?; } } Ok(()) } } impl std::error::Error for GitHubError {} octocrab-0.31.2/src/etag.rs000064400000000000000000000234741046102023000135720ustar 00000000000000//! Types for handling etags. use std::{ fmt::{self, Display}, str::FromStr, }; use http::header::{HeaderMap, InvalidHeaderValue}; use snafu::GenerateImplicitData; /// Represents resources identified by etags. #[derive(Debug, PartialEq)] pub struct Etagged { /// A possible etag. /// /// It is possible, although unlikely, that a response which should contain an etag header does /// not, or that etag header is invalid. In such cases this field will be `None`. pub etag: Option, /// The value identified by this etag. /// /// This can be `None` if we have already received the data which this etag identifies. pub value: Option, } /* * NOTE: The following code was copied from the "hyperx" crate (https://github.com/dekellum/hyperx/), which is no longer a dependency of this project. */ /// An entity tag, defined in [RFC7232](https://tools.ietf.org/html/rfc7232#section-2.3) /// /// An entity tag consists of a string enclosed by two literal double quotes. /// Preceding the first double quote is an optional weakness indicator, /// which always looks like `W/`. Examples for valid tags are `"xyzzy"` and `W/"xyzzy"`. /// /// # ABNF /// /// ```text /// entity-tag = [ weak ] opaque-tag /// weak = %x57.2F ; "W/", case-sensitive /// opaque-tag = DQUOTE *etagc DQUOTE /// etagc = %x21 / %x23-7E / obs-text /// ; VCHAR except double quotes, plus obs-text /// ``` /// /// # Comparison /// To check if two entity tags are equivalent in an application always use the `strong_eq` or /// `weak_eq` methods based on the context of the Tag. Only use `==` to check if two tags are /// identical. /// /// The example below shows the results for a set of entity-tag pairs and /// both the weak and strong comparison function results: /// /// | ETag 1 | ETag 2 | Strong Comparison | Weak Comparison | /// |---------|---------|-------------------|-----------------| /// | `W/"1"` | `W/"1"` | no match | match | /// | `W/"1"` | `W/"2"` | no match | no match | /// | `W/"1"` | `"1"` | no match | match | /// | `"1"` | `"1"` | match | match | #[derive(Clone, Debug, Eq, PartialEq)] pub struct EntityTag { /// Weakness indicator for the tag pub weak: bool, /// The opaque string in between the DQUOTEs tag: String, } impl EntityTag { pub fn extract_from_response(response: &http::Response) -> Option { response .headers() .get("ETag") .and_then(|it| it.to_str().ok()) .and_then(|it| EntityTag::from_str(it).ok()) } pub fn insert_if_none_match_header( headers: &mut HeaderMap, etag: EntityTag, ) -> Result<(), crate::Error> { headers.insert( "If-None-Match", etag.to_string() .parse() .map_err(|err: InvalidHeaderValue| crate::Error::InvalidHeaderValue { source: err, backtrace: snafu::Backtrace::generate(), })?, ); Ok(()) } /// Constructs a new EntityTag. /// # Panics /// If the tag contains invalid characters. pub fn new(weak: bool, tag: String) -> EntityTag { assert!(check_slice_validity(&tag), "Invalid tag: {:?}", tag); EntityTag { weak, tag } } /// Constructs a new weak EntityTag. /// # Panics /// If the tag contains invalid characters. pub fn weak(tag: String) -> EntityTag { EntityTag::new(true, tag) } /// Constructs a new strong EntityTag. /// # Panics /// If the tag contains invalid characters. pub fn strong(tag: String) -> EntityTag { EntityTag::new(false, tag) } /// Get the tag. pub fn tag(&self) -> &str { self.tag.as_ref() } /// Set the tag. /// # Panics /// If the tag contains invalid characters. pub fn set_tag(&mut self, tag: String) { assert!(check_slice_validity(&tag), "Invalid tag: {:?}", tag); self.tag = tag } /// For strong comparison two entity-tags are equivalent if both are not weak and their /// opaque-tags match character-by-character. pub fn strong_eq(&self, other: &EntityTag) -> bool { !self.weak && !other.weak && self.tag == other.tag } /// For weak comparison two entity-tags are equivalent if their /// opaque-tags match character-by-character, regardless of either or /// both being tagged as "weak". pub fn weak_eq(&self, other: &EntityTag) -> bool { self.tag == other.tag } /// The inverse of `EntityTag.strong_eq()`. pub fn strong_ne(&self, other: &EntityTag) -> bool { !self.strong_eq(other) } /// The inverse of `EntityTag.weak_eq()`. pub fn weak_ne(&self, other: &EntityTag) -> bool { !self.weak_eq(other) } } impl Display for EntityTag { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if self.weak { write!(f, "W/\"{}\"", self.tag) } else { write!(f, "\"{}\"", self.tag) } } } /// check that each char in the slice is either: /// 1. `%x21`, or /// 2. in the range `%x23` to `%x7E`, or /// 3. above `%x80` fn check_slice_validity(slice: &str) -> bool { slice .bytes() .all(|c| c == b'\x21' || (b'\x23'..=b'\x7e').contains(&c) | (c >= b'\x80')) } impl FromStr for EntityTag { type Err = String; fn from_str(s: &str) -> Result { let length: usize = s.len(); let slice = s; // Early exits if it doesn't terminate in a DQUOTE. if !slice.ends_with('"') || slice.len() < 2 { return Err("Does not end with double quote character".to_string()); } // The etag is weak if its first char is not a DQUOTE. if slice.len() >= 2 && slice.starts_with('"') && check_slice_validity(&slice[1..length - 1]) { // No need to check if the last char is a DQUOTE, // we already did that above. return Ok(EntityTag { weak: false, tag: slice[1..length - 1].to_owned(), }); } else if slice.len() >= 4 && slice.starts_with("W/\"") && check_slice_validity(&slice[3..length - 1]) { return Ok(EntityTag { weak: true, tag: slice[3..length - 1].to_owned(), }); } Err("Could not parse EntityTag".to_string()) } } #[cfg(test)] mod tests { use super::EntityTag; #[test] fn test_etag_parse_success() { // Expected success assert_eq!( "\"foobar\"".parse::().unwrap(), EntityTag::strong("foobar".to_owned()) ); assert_eq!( "\"\"".parse::().unwrap(), EntityTag::strong("".to_owned()) ); assert_eq!( "W/\"weaktag\"".parse::().unwrap(), EntityTag::weak("weaktag".to_owned()) ); assert_eq!( "W/\"\x65\x62\"".parse::().unwrap(), EntityTag::weak("\x65\x62".to_owned()) ); assert_eq!( "W/\"\"".parse::().unwrap(), EntityTag::weak("".to_owned()) ); } #[test] fn test_etag_parse_failures() { // Expected failures assert!("no-dquotes".parse::().is_err()); assert!("w/\"the-first-w-is-case-sensitive\"" .parse::() .is_err()); assert!("".parse::().is_err()); assert!("\"unmatched-dquotes1".parse::().is_err()); assert!("unmatched-dquotes2\"".parse::().is_err()); assert!("matched-\"dquotes\"".parse::().is_err()); } #[test] fn test_etag_fmt() { assert_eq!( format!("{}", EntityTag::strong("foobar".to_owned())), "\"foobar\"" ); assert_eq!(format!("{}", EntityTag::strong("".to_owned())), "\"\""); assert_eq!( format!("{}", EntityTag::weak("weak-etag".to_owned())), "W/\"weak-etag\"" ); assert_eq!( format!("{}", EntityTag::weak("\u{0065}".to_owned())), "W/\"\x65\"" ); assert_eq!(format!("{}", EntityTag::weak("".to_owned())), "W/\"\""); } #[test] fn test_cmp() { // | ETag 1 | ETag 2 | Strong Comparison | Weak Comparison | // |---------|---------|-------------------|-----------------| // | `W/"1"` | `W/"1"` | no match | match | // | `W/"1"` | `W/"2"` | no match | no match | // | `W/"1"` | `"1"` | no match | match | // | `"1"` | `"1"` | match | match | let mut etag1 = EntityTag::weak("1".to_owned()); let mut etag2 = EntityTag::weak("1".to_owned()); assert!(!etag1.strong_eq(&etag2)); assert!(etag1.weak_eq(&etag2)); assert!(etag1.strong_ne(&etag2)); assert!(!etag1.weak_ne(&etag2)); etag1 = EntityTag::weak("1".to_owned()); etag2 = EntityTag::weak("2".to_owned()); assert!(!etag1.strong_eq(&etag2)); assert!(!etag1.weak_eq(&etag2)); assert!(etag1.strong_ne(&etag2)); assert!(etag1.weak_ne(&etag2)); etag1 = EntityTag::weak("1".to_owned()); etag2 = EntityTag::strong("1".to_owned()); assert!(!etag1.strong_eq(&etag2)); assert!(etag1.weak_eq(&etag2)); assert!(etag1.strong_ne(&etag2)); assert!(!etag1.weak_ne(&etag2)); etag1 = EntityTag::strong("1".to_owned()); etag2 = EntityTag::strong("1".to_owned()); assert!(etag1.strong_eq(&etag2)); assert!(etag1.weak_eq(&etag2)); assert!(!etag1.strong_ne(&etag2)); assert!(!etag1.weak_ne(&etag2)); } } octocrab-0.31.2/src/from_response.rs000064400000000000000000000013501046102023000155200ustar 00000000000000use snafu::ResultExt; /// A trait for mapping from a `http::Response` to an another type. #[async_trait::async_trait] pub trait FromResponse: Sized { async fn from_response(response: http::Response) -> crate::Result; } #[async_trait::async_trait] impl FromResponse for T { async fn from_response(response: http::Response) -> crate::Result { let (_, body) = response.into_parts(); let body = hyper::body::to_bytes(body) .await .context(crate::error::HyperSnafu)?; let de = &mut serde_json::Deserializer::from_slice(&body); return serde_path_to_error::deserialize(de).context(crate::error::JsonSnafu); } } octocrab-0.31.2/src/lib.rs000064400000000000000000001610661046102023000134200ustar 00000000000000//! # Octocrab: A modern, extensible GitHub API client. //! Octocrab is an third party GitHub API client, allowing you to easily build //! your own GitHub integrations or bots. `octocrab` comes with two primary //! set of APIs for communicating with GitHub, a high level strongly typed //! semantic API, and a lower level HTTP API for extending behaviour. //! //! ## Semantic API //! The semantic API provides strong typing around GitHub's API, as well as a //! set of [`models`] that maps to GitHub's types. Currently the following //! modules are available. //! //! - [`activity`] GitHub Activity //! - [`actions`] GitHub Actions //! - [`apps`] GitHub Apps //! - [`current`] Information about the current user. //! - [`gitignore`] Gitignore templates //! - [`Octocrab::graphql`] GraphQL. //! - [`issues`] Issues and related items, e.g. comments, labels, etc. //! - [`licenses`] License Metadata. //! - [`markdown`] Rendering Markdown with GitHub //! - [`orgs`] GitHub Organisations //! - [`projects`] GitHub Projects //! - [`pulls`] Pull Requests //! - [`repos`] Repositories //! - [`repos::forks`] Repositories //! - [`repos::releases`] Repositories //! - [`search`] Using GitHub's search. //! - [`teams`] Teams //! - [`users`] Users //! //! #### Getting a Pull Request //! ```no_run //! # async fn run() -> octocrab::Result<()> { //! // Get pull request #404 from `octocrab/repo`. //! let pr = octocrab::instance().pulls("octocrab", "repo").get(404).await?; //! # Ok(()) //! # } //! ``` //! //! All methods with multiple optional parameters are built as `Builder` //! structs, allowing you to easily specify parameters. //! //! #### Listing issues //! ```no_run //! # async fn run() -> octocrab::Result<()> { //! use octocrab::{models, params}; //! //! let octocrab = octocrab::instance(); //! // Returns the first page of all issues. //! let mut page = octocrab.issues("octocrab", "repo") //! .list() //! // Optional Parameters //! .creator("octocrab") //! .state(params::State::All) //! .per_page(50) //! .send() //! .await?; //! //! // Go through every page of issues. Warning: There's no rate limiting so //! // be careful. //! let results = octocrab.all_pages::(page).await?; //! //! # Ok(()) //! # } //! ``` //! //! ## HTTP API //! The typed API currently doesn't cover all of GitHub's API at this time, and //! even if it did GitHub is in active development and this library will //! likely always be somewhat behind GitHub at some points in time. However that //! shouldn't mean that in order to use those features that you have to now fork //! or replace `octocrab` with your own solution. //! //! Instead `octocrab` exposes a suite of HTTP methods allowing you to easily //! extend `Octocrab`'s existing behaviour. Using these HTTP methods allows you //! to keep using the same authentication and configuration, while having //! control over the request and response. There is a method for each HTTP //! method `get`, `post`, `patch`, `put`, `delete`, all of which accept a //! relative route and a optional body. //! //! ```no_run //! # async fn run() -> octocrab::Result<()> { //! let user: octocrab::models::Author = octocrab::instance() //! .get("/user", None::<&()>) //! .await?; //! # Ok(()) //! # } //! ``` //! //! Each of the HTTP methods expects a body, formats the URL with the base //! URL, and errors if GitHub doesn't return a successful status, but this isn't //! always desired when working with GitHub's API, sometimes you need to check //! the response status or headers. As such there are companion methods `_get`, //! `_post`, etc. that perform no additional pre or post-processing to //! the request. //! //! ```no_run //! # use http::Uri; //! # async fn run() -> octocrab::Result<()> { //! let octocrab = octocrab::instance(); //! let response = octocrab //! ._get("https://api.github.com/organizations") //! .await?; //! //! // You can also use `Uri::builder().authority("").path_and_query("")` if you want to customize the base uri and path. //! let response = octocrab //! ._get(Uri::builder().path_and_query("/organizations").build().expect("valid uri")) //! .await?; //! # Ok(()) //! # } //! ``` //! //! You can use the those HTTP methods to easily create your own extensions to //! `Octocrab`'s typed API. (Requires `async_trait`). //! ``` //! use octocrab::{Octocrab, Page, Result, models}; //! //! #[async_trait::async_trait] //! trait OrganisationExt { //! async fn list_every_organisation(&self) -> Result>; //! } //! //! #[async_trait::async_trait] //! impl OrganisationExt for Octocrab { //! async fn list_every_organisation(&self) -> Result> { //! self.get("organizations", None::<&()>).await //! } //! } //! ``` //! //! You can also easily access new properties that aren't available in the //! current models using `serde`. //! //! ## Static API //! `octocrab` also provides a statically reference count version of its API, //! allowing you to easily plug it into existing systems without worrying //! about having to integrate and pass around the client. //! //! ``` //! // Initialises the static instance with your configuration and returns an //! // instance of the client. //! # use octocrab::Octocrab; //! tokio_test::block_on(async { //! octocrab::initialise(Octocrab::default()); //! // Gets a instance of `Octocrab` from the static API. If you call this //! // without first calling `octocrab::initialise` a default client will be //! // initialised and returned instead. //! octocrab::instance(); //! # }) //! ``` //! //! ## GitHub webhook application support //! //! `octocrab` provides [deserializable datatypes](crate::models::webhook_events) //! for the payloads received by a GitHub application [responding to //! webhooks](https://docs.github.com/en/apps/creating-github-apps/writing-code-for-a-github-app/building-a-github-app-that-responds-to-webhook-events). //! This allows you to write a typesafe application using Rust with //! pattern-matching/enum-dispatch to respond to events. //! //! **Note**: Webhook support in `octocrab` is still beta, not all known webhook events are //! strongly typed. //! //! ```no_run //! # use http::request::Request; //! # use tracing::{warn, info}; //! # use octocrab::models::webhook_events::*; //! # let request_from_github = Request::post("https://my-webhook-url.com").body(vec![0_u8]).unwrap(); //! // request_from_github is the HTTP request your webhook handler received //! let (parts, body) = request_from_github.into_parts(); //! let header = parts.headers.get("X-GitHub-Event").unwrap().to_str().unwrap(); //! //! let event = WebhookEvent::try_from_header_and_body(header, &body).unwrap(); //! // Now you can match on event type and call any specific handling logic //! match event.kind { //! WebhookEventType::Ping => info!("Received a ping"), //! WebhookEventType::PullRequest => info!("Received a pull request event"), //! // ... //! _ => warn!("Ignored event"), //! }; //! ``` #![cfg_attr(test, recursion_limit = "512")] mod api; mod error; mod from_response; mod page; pub mod auth; pub mod etag; pub mod models; pub mod params; pub mod service; use crate::service::body::BodyStreamExt; use chrono::{DateTime, Utc}; use http::{HeaderMap, HeaderValue, Method, Uri}; use std::convert::{Infallible, TryInto}; use std::fmt; use std::io::Write; use std::marker::PhantomData; use std::str::FromStr; use std::sync::{Arc, RwLock}; use std::time::Duration; use http::{header::HeaderName, StatusCode}; #[cfg(all(not(feature = "opentls"), not(feature = "rustls")))] use hyper::client::HttpConnector; use hyper::{body, Body, Request, Response}; use once_cell::sync::Lazy; use secrecy::{ExposeSecret, SecretString}; use serde::Serialize; use snafu::*; use tower::{buffer::Buffer, util::BoxService, BoxError, Layer, Service, ServiceExt}; use bytes::Bytes; use http::header::USER_AGENT; use http::request::Builder; #[cfg(feature = "opentls")] use hyper_tls::HttpsConnector; #[cfg(feature = "rustls")] use hyper_rustls::HttpsConnectorBuilder; #[cfg(feature = "retry")] use tower::retry::{Retry, RetryLayer}; #[cfg(feature = "timeout")] use { hyper_timeout::TimeoutConnector, tokio::io::{AsyncRead, AsyncWrite}, }; use tower_http::{classify::ServerErrorsFailureClass, map_response_body::MapResponseBodyLayer}; #[cfg(feature = "tracing")] use {tower_http::trace::TraceLayer, tracing::Span}; use crate::error::{ HttpSnafu, HyperSnafu, InvalidUtf8Snafu, SerdeSnafu, SerdeUrlEncodedSnafu, ServiceSnafu, UriParseError, UriParseSnafu, UriSnafu, }; use crate::service::middleware::base_uri::BaseUriLayer; use crate::service::middleware::extra_headers::ExtraHeadersLayer; #[cfg(feature = "retry")] use crate::service::middleware::retry::RetryConfig; use crate::api::users; use auth::{AppAuth, Auth}; use models::{AppId, InstallationId, InstallationToken}; pub use self::{ api::{ actions, activity, apps, checks, commits, current, events, gists, gitignore, issues, licenses, markdown, orgs, projects, pulls, ratelimit, repos, search, teams, workflows, }, error::{Error, GitHubError}, from_response::FromResponse, page::Page, }; /// A convenience type with a default error type of [`Error`]. pub type Result = std::result::Result; const GITHUB_BASE_URI: &str = "https://api.github.com"; static STATIC_INSTANCE: Lazy> = Lazy::new(|| arc_swap::ArcSwap::from_pointee(Octocrab::default())); /// Formats a GitHub preview from it's name into the full value for the /// `Accept` header. /// ``` /// assert_eq!(octocrab::format_preview("machine-man"), "application/vnd.github.machine-man-preview"); /// ``` pub fn format_preview(preview: impl AsRef) -> String { format!("application/vnd.github.{}-preview", preview.as_ref()) } /// Formats a media type from it's name into the full value for the /// `Accept` header. /// ``` /// assert_eq!(octocrab::format_media_type("html"), "application/vnd.github.v3.html+json"); /// assert_eq!(octocrab::format_media_type("json"), "application/vnd.github.v3.json"); /// assert_eq!(octocrab::format_media_type("patch"), "application/vnd.github.v3.patch"); /// ``` pub fn format_media_type(media_type: impl AsRef) -> String { let media_type = media_type.as_ref(); let json_suffix = match media_type { "raw" | "text" | "html" | "full" => "+json", _ => "", }; format!("application/vnd.github.v3.{media_type}{json_suffix}") } /// Maps a GitHub error response into and `Err()` variant if the status is /// not a success. pub async fn map_github_error( response: http::Response, ) -> Result> { if response.status().is_success() { Ok(response) } else { let b: error::GitHubError = serde_json::from_slice( body::to_bytes(response.into_body()) .await .context(error::HyperSnafu)? .as_ref(), ) .context(error::SerdeSnafu)?; Err(error::Error::GitHub { source: b, backtrace: Backtrace::generate(), }) } } /// Initialises the static instance using the configuration set by /// `builder`. /// ``` /// # #[tokio::main] /// # async fn main() -> Result<(), Box> { /// let octocrab = octocrab::initialise(octocrab::Octocrab::default()); /// # Ok(()) /// # } /// ``` pub fn initialise(crab: Octocrab) -> Arc { STATIC_INSTANCE.swap(Arc::from(crab)) } /// Returns a new instance of [`Octocrab`]. If it hasn't been previously /// initialised it returns a default instance with no authentication set. /// ``` /// #[tokio::main] /// async fn main() -> () { /// let octocrab = octocrab::instance(); /// } /// ``` pub fn instance() -> Arc { STATIC_INSTANCE.load().clone() } /// A builder struct for `Octocrab`, allowing you to configure the client, such /// as using GitHub previews, the github instance, authentication, etc. /// ``` /// # #[tokio::main] /// # async fn main() -> Result<(), Box> { /// let octocrab = octocrab::OctocrabBuilder::default() /// .add_preview("machine-man") /// .base_uri("https://github.example.com")? /// .build()?; /// # Ok(()) /// # } /// ``` //Typed builder, thanks to https://www.greyblake.com/blog/builder-with-typestate-in-rust/ for explaining /// A builder struct for `Octocrab`. /// OctocrabBuilder can be extended with a custom config, see [DefaultOctocrabBuilderConfig] for an example pub struct OctocrabBuilder { service: Svc, auth: Auth, config: Config, _layer_ready: PhantomData, } //Indicates weather the builder supports config pub struct NoConfig {} //Indicates weather the builder supports service that is already inside builder pub struct NoSvc {} //Indicates weather builder supports with_layer(This is somewhat redundant given NoSvc exists, but we have to use this until specialization is stable) pub struct NotLayerReady {} pub struct LayerReady {} //Indicates weather the builder supports auth pub struct NoAuth {} impl OctocrabBuilder { pub fn new_empty() -> Self { OctocrabBuilder { service: NoSvc {}, auth: NoAuth {}, config: NoConfig {}, _layer_ready: PhantomData, } } } impl OctocrabBuilder { pub fn new() -> Self { OctocrabBuilder::default() } } impl OctocrabBuilder { pub fn with_service(self, service: Svc) -> OctocrabBuilder { OctocrabBuilder { service, auth: self.auth, config: self.config, _layer_ready: PhantomData, } } } impl OctocrabBuilder where Svc: Service, Response = Response> + Send + 'static, Svc::Future: Send + 'static, Svc::Error: Into, B: http_body::Body + Send + 'static, B::Error: Into, { /// Add a [`Layer`] to the current [`Service`] stack. pub fn with_layer>( self, layer: &L, ) -> OctocrabBuilder { let Self { service: stack, auth, config, .. } = self; OctocrabBuilder { service: layer.layer(stack), auth, config, _layer_ready: PhantomData, } } } impl Default for OctocrabBuilder { fn default() -> OctocrabBuilder { OctocrabBuilder::new_empty().with_config(DefaultOctocrabBuilderConfig::default()) } } impl OctocrabBuilder { fn with_config(self, config: Config) -> OctocrabBuilder { OctocrabBuilder { service: self.service, auth: self.auth, config, _layer_ready: PhantomData, } } } impl OctocrabBuilder where Svc: Service, Response = Response> + Send + 'static, Svc::Future: Send + 'static, Svc::Error: Into, B: http_body::Body + Send + 'static, B::Error: Into, { /// Build a [`Client`] instance with the current [`Service`] stack. pub fn build(self) -> Result { Ok(Octocrab::new(self.service, self.auth)) } } impl OctocrabBuilder { pub fn with_auth(self, auth: Auth) -> OctocrabBuilder { OctocrabBuilder { service: self.service, auth, config: self.config, _layer_ready: PhantomData, } } } impl OctocrabBuilder { #[cfg(feature = "retry")] pub fn add_retry_config(&mut self, retry_config: RetryConfig) -> &mut Self { self.config.retry_config = retry_config; self } /// Enable a GitHub preview. pub fn add_preview(mut self, preview: &'static str) -> Self { self.config.previews.push(preview); self } /// Add an additional header to include with every request. pub fn add_header(mut self, key: HeaderName, value: String) -> Self { self.config.extra_headers.push((key, value)); self } /// Add a personal token to use for authentication. pub fn personal_token(mut self, token: String) -> Self { self.config.auth = Auth::PersonalToken(SecretString::new(token)); self } /// Authenticate as a Github App. /// `key`: RSA private key in DER or PEM formats. pub fn app(mut self, app_id: AppId, key: jsonwebtoken::EncodingKey) -> Self { self.config.auth = Auth::App(AppAuth { app_id, key }); self } /// Authenticate as a Basic Auth /// username and password pub fn basic_auth(mut self, username: String, password: String) -> Self { self.config.auth = Auth::Basic { username, password }; self } /// Authenticate with an OAuth token. pub fn oauth(mut self, oauth: auth::OAuth) -> Self { self.config.auth = Auth::OAuth(oauth); self } /// Authenticate with a user access token. pub fn user_access_token(mut self, token: String) -> Self { self.config.auth = Auth::UserAccessToken(SecretString::new(token)); self } /// Set the base url for `Octocrab`. pub fn base_uri(mut self, base_uri: impl TryInto) -> Result { self.config.base_uri = Some( base_uri .try_into() .map_err(|_| UriParseError {}) .context(UriParseSnafu)?, ); Ok(self) } #[cfg(feature = "retry")] pub fn set_connector_retry_service( &self, connector: hyper::Client, ) -> Retry> { let retry_layer = RetryLayer::new(self.config.retry_config.clone()); retry_layer.layer(connector) } #[cfg(feature = "timeout")] pub fn set_connect_timeout_service(&self, connector: T) -> TimeoutConnector where T: Service + Send, T::Response: AsyncRead + AsyncWrite + Send + Unpin, T::Future: Send + 'static, T::Error: Into, { let mut connector = TimeoutConnector::new(connector); // Set the timeouts for the client connector.set_connect_timeout(self.config.connect_timeout); connector.set_read_timeout(self.config.read_timeout); connector.set_write_timeout(self.config.write_timeout); connector } /// Build a [`Client`] instance with the current [`Service`] stack. pub fn build(self) -> Result { let client: hyper::Client<_, String> = { #[cfg(all(not(feature = "opentls"), not(feature = "rustls")))] let mut connector = HttpConnector::new(); #[cfg(all(feature = "rustls", not(feature = "opentls")))] let connector = { let builder = HttpsConnectorBuilder::new(); #[cfg(feature = "rustls-webpki-tokio")] let builder = builder.with_webpki_roots(); #[cfg(not(feature = "rustls-webpki-tokio"))] let builder = builder.with_native_roots(); // enabled the `rustls-native-certs` feature in hyper-rustls builder .https_or_http() // Disable .https_only() during tests until: https://github.com/LukeMathWalker/wiremock-rs/issues/58 is resolved. Alternatively we can use conditional compilation to only enable this feature in tests, but it becomes rather ugly with integration tests. .enable_http1() .build() }; #[cfg(all(feature = "opentls", not(feature = "rustls")))] let connector = HttpsConnector::new(); #[cfg(feature = "timeout")] let connector = self.set_connect_timeout_service(connector); hyper::Client::builder().build(connector) }; #[cfg(feature = "retry")] let client = self.set_connector_retry_service(client); #[cfg(feature = "tracing")] let client = TraceLayer::new_for_http() .make_span_with(|req: &Request| { tracing::debug_span!( "HTTP", http.method = %req.method(), http.url = %req.uri(), http.status_code = tracing::field::Empty, otel.name = req.extensions().get::<&'static str>().unwrap_or(&"HTTP"), otel.kind = "client", otel.status_code = tracing::field::Empty, ) }) .on_request(|_req: &Request, _span: &Span| { tracing::debug!("requesting"); }) .on_response( |res: &Response, _latency: Duration, span: &Span| { let status = res.status(); span.record("http.status_code", status.as_u16()); if status.is_client_error() || status.is_server_error() { span.record("otel.status_code", "ERROR"); } }, ) // Explicitly disable `on_body_chunk`. The default does nothing. .on_body_chunk(()) .on_eos(|_: Option<&HeaderMap>, _duration: Duration, _span: &Span| { tracing::debug!("stream closed"); }) .on_failure( |ec: ServerErrorsFailureClass, _latency: Duration, span: &Span| { // Called when // - Calling the inner service errored // - Polling `Body` errored // - the response was classified as failure (5xx) // - End of stream was classified as failure span.record("otel.status_code", "ERROR"); match ec { ServerErrorsFailureClass::StatusCode(status) => { span.record("http.status_code", status.as_u16()); tracing::error!("failed with status {}", status) } ServerErrorsFailureClass::Error(err) => { tracing::error!("failed with error {}", err) } } }, ) .layer(client); #[cfg(feature = "follow-redirect")] let client = tower_http::follow_redirect::FollowRedirectLayer::new().layer(client); let mut hmap: Vec<(HeaderName, HeaderValue)> = vec![]; // Add the user agent header required by GitHub hmap.push((USER_AGENT, HeaderValue::from_str("octocrab").unwrap())); for preview in &self.config.previews { hmap.push(( http::header::ACCEPT, HeaderValue::from_str(crate::format_preview(preview).as_str()).unwrap(), )); } let auth_state = match self.config.auth { Auth::None => AuthState::None, Auth::Basic { username, password } => AuthState::BasicAuth { username, password }, Auth::PersonalToken(token) => { hmap.push(( http::header::AUTHORIZATION, format!("Bearer {}", token.expose_secret()).parse().unwrap(), )); AuthState::None } Auth::UserAccessToken(token) => { hmap.push(( http::header::AUTHORIZATION, format!("Bearer {}", token.expose_secret()).parse().unwrap(), )); AuthState::None } Auth::App(app_auth) => AuthState::App(app_auth), Auth::OAuth(device) => { hmap.push(( http::header::AUTHORIZATION, format!( "{} {}", device.token_type, &device.access_token.expose_secret() ) .parse() .unwrap(), )); AuthState::None } }; for (key, value) in self.config.extra_headers.iter() { hmap.push(( key.clone(), HeaderValue::from_str(value.as_str()) .map_err(http::Error::from) .context(HttpSnafu)?, )); } let client = ExtraHeadersLayer::new(Arc::new(hmap)).layer(client); let client = MapResponseBodyLayer::new(|body| { Box::new(http_body::Body::map_err(body, BoxError::from)) as Box }) .layer(client); let uri = self .config .base_uri .clone() .unwrap_or_else(|| Uri::from_str(GITHUB_BASE_URI).unwrap()); let client = BaseUriLayer::new(uri).layer(client); Ok(Octocrab::new(client, auth_state)) } } pub struct DefaultOctocrabBuilderConfig { auth: Auth, previews: Vec<&'static str>, extra_headers: Vec<(HeaderName, String)>, #[cfg(feature = "timeout")] connect_timeout: Option, #[cfg(feature = "timeout")] read_timeout: Option, #[cfg(feature = "timeout")] write_timeout: Option, base_uri: Option, #[cfg(feature = "retry")] retry_config: RetryConfig, } impl Default for DefaultOctocrabBuilderConfig { fn default() -> Self { Self { auth: Auth::None, previews: Vec::new(), extra_headers: Vec::new(), #[cfg(feature = "timeout")] connect_timeout: None, #[cfg(feature = "timeout")] read_timeout: None, #[cfg(feature = "timeout")] write_timeout: None, base_uri: None, #[cfg(feature = "retry")] retry_config: RetryConfig::Simple(3), } } } impl DefaultOctocrabBuilderConfig { pub fn new() -> Self { Self::default() } } pub type DynBody = dyn http_body::Body + Send + Unpin; #[derive(Debug, Clone)] struct CachedTokenInner { expiration: Option>, secret: SecretString, } impl CachedTokenInner { fn new(secret: SecretString, expiration: Option>) -> Self { Self { secret, expiration } } fn expose_secret(&self) -> &str { self.secret.expose_secret() } } /// A cached API access token (which may be None) pub struct CachedToken(RwLock>); impl CachedToken { fn clear(&self) { *self.0.write().unwrap() = None; } /// Returns a valid token if it exists and is not expired or if there is no expiration date. fn valid_token_with_buffer(&self, buffer: chrono::Duration) -> Option { let inner = self.0.read().unwrap(); if let Some(token) = inner.as_ref() { if let Some(exp) = token.expiration { if exp - Utc::now() > buffer { return Some(token.secret.clone()); } } else { return Some(token.secret.clone()); } } None } fn valid_token(&self) -> Option { self.valid_token_with_buffer(chrono::Duration::seconds(30)) } fn set(&self, token: String, expiration: Option>) { *self.0.write().unwrap() = Some(CachedTokenInner::new(SecretString::new(token), expiration)); } } impl fmt::Debug for CachedToken { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.read().unwrap().fmt(f) } } impl fmt::Display for CachedToken { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let option = self.0.read().unwrap(); option .as_ref() .map(|s| s.expose_secret().fmt(f)) .unwrap_or_else(|| write!(f, "")) } } impl Clone for CachedToken { fn clone(&self) -> CachedToken { CachedToken(RwLock::new(self.0.read().unwrap().clone())) } } impl Default for CachedToken { fn default() -> CachedToken { CachedToken(RwLock::new(None)) } } /// State used for authenticate to Github #[derive(Debug, Clone)] pub enum AuthState { /// No state, although Auth::PersonalToken may have caused /// an Authorization HTTP header to be set to provide authentication. None, /// Basic Auth HTTP. (username:password) BasicAuth { /// The username username: String, /// The password password: String, }, /// Github App authentication with the given app data App(AppAuth), /// Authentication via a Github App repo-specific installation Installation { /// The app authentication data (app ID and private key) app: AppAuth, /// The installation ID installation: InstallationId, /// The cached access token, if any token: CachedToken, }, } pub type OctocrabService = Buffer< BoxService, http::Response, BoxError>, http::Request, >; /// The GitHub API client. #[derive(Clone)] pub struct Octocrab { client: OctocrabService, auth_state: AuthState, } impl fmt::Debug for Octocrab { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Octocrab") .field("auth_state", &self.auth_state) .finish() } } /// Defaults for Octocrab: /// - `base_uri`: `https://api.github.com` /// - `auth`: `None` /// - `client`: http client with the `octocrab` user agent. impl Default for Octocrab { fn default() -> Self { OctocrabBuilder::default().build().unwrap() } } /// # Constructors impl Octocrab { /// Returns a new `OctocrabBuilder`. pub fn builder() -> OctocrabBuilder { OctocrabBuilder::new_empty().with_config(DefaultOctocrabBuilderConfig::default()) } /// Creates a new `Octocrab`. fn new(service: S, auth_state: AuthState) -> Self where S: Service, Response = Response> + Send + 'static, S::Future: Send + 'static, S::Error: Into, B: http_body::Body + Send + 'static, B::Error: Into, { // Transform response body to `hyper::Body` and use type erased error to avoid type parameters. let service = MapResponseBodyLayer::new(|b: B| Body::wrap_stream(b.into_stream())) .layer(service) .map_err(|e| e.into()); let service = Buffer::new(BoxService::new(service), 1024); Self { client: service, auth_state, } } /// Returns a new `Octocrab` based on the current builder but /// authorizing via a specific installation ID. /// Typically you will first construct an `Octocrab` using /// `OctocrabBuilder::app` to authenticate as your Github App, /// then obtain an installation ID, and then pass that here to /// obtain a new `Octocrab` with which you can make API calls /// with the permissions of that installation. pub fn installation(&self, id: InstallationId) -> Octocrab { let app_auth = if let AuthState::App(ref app_auth) = self.auth_state { app_auth.clone() } else { panic!("Github App authorization is required to target an installation"); }; Octocrab { client: self.client.clone(), auth_state: AuthState::Installation { app: app_auth, installation: id, token: CachedToken::default(), }, } } /// Similar to `installation`, but also eagerly caches the installation /// token and returns the token. The returned token can be used to make /// https git requests to e.g. clone repositories that the installation /// has access to. /// /// See also https://docs.github.com/en/developers/apps/building-github-apps/authenticating-with-github-apps#http-based-git-access-by-an-installation pub async fn installation_and_token( &self, id: InstallationId, ) -> Result<(Octocrab, SecretString)> { let crab = self.installation(id); let token = crab.request_installation_auth_token().await?; Ok((crab, token)) } } /// # GitHub API Methods impl Octocrab { /// Creates a new [`actions::ActionsHandler`] for accessing information from /// GitHub Actions. pub fn actions(&self) -> actions::ActionsHandler { actions::ActionsHandler::new(self) } /// Creates a [`current::CurrentAuthHandler`] that allows you to access /// information about the current authenticated user. pub fn current(&self) -> current::CurrentAuthHandler { current::CurrentAuthHandler::new(self) } /// Creates a [`activity::ActivityHandler`] for the current authenticated user. pub fn activity(&self) -> activity::ActivityHandler { activity::ActivityHandler::new(self) } /// Creates a new [`apps::AppsRequestHandler`] for the currently authenticated app. pub fn apps(&self) -> apps::AppsRequestHandler { apps::AppsRequestHandler::new(self) } /// Creates a [`gitignore::GitignoreHandler`] for accessing information /// about `gitignore`. pub fn gitignore(&self) -> gitignore::GitignoreHandler { gitignore::GitignoreHandler::new(self) } /// Creates a [`issues::IssueHandler`] for the repo specified at `owner/repo`, /// that allows you to access GitHub's issues API. pub fn issues( &self, owner: impl Into, repo: impl Into, ) -> issues::IssueHandler { issues::IssueHandler::new(self, owner.into(), repo.into()) } /// Creates a [`commits::CommitHandler`] for the repo specified at `owner/repo`, pub fn commits( &self, owner: impl Into, repo: impl Into, ) -> commits::CommitHandler { commits::CommitHandler::new(self, owner.into(), repo.into()) } /// Creates a [`licenses::LicenseHandler`]. pub fn licenses(&self) -> licenses::LicenseHandler { licenses::LicenseHandler::new(self) } /// Creates a [`markdown::MarkdownHandler`]. pub fn markdown(&self) -> markdown::MarkdownHandler { markdown::MarkdownHandler::new(self) } /// Creates an [`orgs::OrgHandler`] for the specified organization, /// that allows you to access GitHub's organization API. pub fn orgs(&self, owner: impl Into) -> orgs::OrgHandler { orgs::OrgHandler::new(self, owner.into()) } /// Creates a [`pulls::PullRequestHandler`] for the repo specified at /// `owner/repo`, that allows you to access GitHub's pull request API. pub fn pulls( &self, owner: impl Into, repo: impl Into, ) -> pulls::PullRequestHandler { pulls::PullRequestHandler::new(self, owner.into(), repo.into()) } /// Creates a [`repos::RepoHandler`] for the repo specified at `owner/repo`, /// that allows you to access GitHub's repository API. pub fn repos(&self, owner: impl Into, repo: impl Into) -> repos::RepoHandler { repos::RepoHandler::new(self, owner.into(), repo.into()) } /// Creates a [`projects::ProjectHandler`] that allows you to access GitHub's /// projects API (classic). pub fn projects(&self) -> projects::ProjectHandler { projects::ProjectHandler::new(self) } /// Creates a [`search::SearchHandler`] that allows you to construct general queries /// to GitHub's API. pub fn search(&self) -> search::SearchHandler { search::SearchHandler::new(self) } /// Creates a [`teams::TeamHandler`] for the specified organization that allows /// you to access GitHub's teams API. pub fn teams(&self, owner: impl Into) -> teams::TeamHandler { teams::TeamHandler::new(self, owner.into()) } /// Creates a [`users::UserHandler`] for the specified user pub fn users(&self, user: impl Into) -> users::UserHandler { users::UserHandler::new(self, user.into()) } /// Creates a [`workflows::WorkflowsHandler`] for the specified repository that allows /// you to access GitHub's workflows API. pub fn workflows( &self, owner: impl Into, repo: impl Into, ) -> workflows::WorkflowsHandler { workflows::WorkflowsHandler::new(self, owner.into(), repo.into()) } /// Creates an [`events::EventsBuilder`] that allows you to access /// GitHub's events API. pub fn events(&self) -> events::EventsBuilder { events::EventsBuilder::new(self) } /// Creates a [`gists::GistsHandler`] that allows you to access /// GitHub's Gists API. pub fn gists(&self) -> gists::GistsHandler { gists::GistsHandler::new(self) } /// Creates a [`checks::ChecksHandler`] that allows to access the Checks API. pub fn checks( &self, owner: impl Into, repo: impl Into, ) -> checks::ChecksHandler { checks::ChecksHandler::new(self, owner.into(), repo.into()) } /// Creates a [`ratelimit::RateLimitHandler`] that returns the API rate limit. pub fn ratelimit(&self) -> ratelimit::RateLimitHandler { ratelimit::RateLimitHandler::new(self) } } /// # GraphQL API. impl Octocrab { /// Sends a graphql query to GitHub, and deserialises the response /// from JSON. /// ```no_run ///# async fn run() -> octocrab::Result<()> { /// let response: serde_json::Value = octocrab::instance() /// .graphql(&serde_json::json!({ "query": "{ viewer { login }}" })) /// .await?; ///# Ok(()) ///# } /// ``` pub async fn graphql( &self, payload: &(impl serde::Serialize + ?Sized), ) -> crate::Result { self.post("/graphql", Some(&serde_json::json!(payload))) .await } } /// # HTTP Methods /// A collection of different of HTTP methods to use with Octocrab's /// configuration (Authenication, etc.). All of the HTTP methods (`get`, `post`, /// etc.) perform some amount of pre-processing such as making relative urls /// absolute, and post processing such as mapping any potential GitHub errors /// into `Err()` variants, and deserializing the response body. /// /// This isn't always ideal when working with GitHub's API and as such there are /// additional methods available prefixed with `_` (e.g. `_get`, `_post`, /// etc.) that perform no pre or post processing and directly return the /// `http::Response` struct. impl Octocrab { /// Send a `POST` request to `route` with an optional body, returning the body /// of the response. pub async fn post( &self, route: impl AsRef, body: Option<&P>, ) -> Result { let response = self ._post(self.parameterized_uri(route, None::<&()>)?, body) .await?; R::from_response(crate::map_github_error(response).await?).await } /// Send a `POST` request with no additional pre/post-processing. pub async fn _post( &self, uri: impl TryInto, body: Option<&P>, ) -> Result> { let uri = uri .try_into() .map_err(|_| UriParseError {}) .context(UriParseSnafu)?; let request = Builder::new().method(Method::POST).uri(uri); let request = self.build_request(request, body)?; self.execute(request).await } /// Send a `GET` request to `route` with optional query parameters, returning /// the body of the response. pub async fn get(&self, route: A, parameters: Option<&P>) -> Result where A: AsRef, P: Serialize + ?Sized, R: FromResponse, { self.get_with_headers(route, parameters, None).await } /// Send a `GET` request with no additional post-processing. pub async fn _get(&self, uri: impl TryInto) -> Result> { self._get_with_headers(uri, None).await } /// Convenience method to accept any &str, and attempt to convert it to a Uri. /// the method also attempts to serialize any parameters into a query string, and append it to the uri. fn parameterized_uri(&self, uri: A, parameters: Option<&P>) -> Result where A: AsRef, P: Serialize + ?Sized, { let mut uri = uri.as_ref().to_string(); if let Some(parameters) = parameters { if uri.contains('?') { uri = format!("{uri}&"); } else { uri = format!("{uri}?"); } uri = format!( "{}{}", uri, serde_urlencoded::to_string(parameters) .context(SerdeUrlEncodedSnafu)? .as_str() ); } let uri = Uri::from_str(uri.as_str()).context(UriSnafu); uri } pub async fn body_to_string(&self, res: http::Response) -> Result { let body_bytes = body::to_bytes(res.into_body()).await.context(HyperSnafu)?; String::from_utf8(body_bytes.to_vec()).context(InvalidUtf8Snafu) } /// Send a `GET` request to `route` with optional query parameters and headers, returning /// the body of the response. pub async fn get_with_headers( &self, route: A, parameters: Option<&P>, headers: Option, ) -> Result where A: AsRef, P: Serialize + ?Sized, R: FromResponse, { let response = self ._get_with_headers(self.parameterized_uri(route, parameters)?, headers) .await?; R::from_response(crate::map_github_error(response).await?).await } /// Send a `GET` request including option to set headers, with no additional post-processing. pub async fn _get_with_headers( &self, uri: impl TryInto, headers: Option, ) -> Result> { let uri = uri .try_into() .map_err(|_| UriParseError {}) .context(UriParseSnafu)?; let mut request = Builder::new().method(Method::GET).uri(uri); if let Some(headers) = headers { for (key, value) in headers.iter() { request = request.header(key, value); } } let request = self.build_request(request, None::<&()>)?; self.execute(request).await } /// Send a `PATCH` request to `route` with optional query parameters, /// returning the body of the response. pub async fn patch(&self, route: A, body: Option<&B>) -> Result where A: AsRef, B: Serialize + ?Sized, R: FromResponse, { let response = self ._patch(self.parameterized_uri(route, None::<&()>)?, body) .await?; R::from_response(crate::map_github_error(response).await?).await } /// Send a `PATCH` request with no additional post-processing. pub async fn _patch( &self, uri: impl TryInto, body: Option<&B>, ) -> Result> { let uri = uri .try_into() .map_err(|_| UriParseError {}) .context(UriParseSnafu)?; let request = Builder::new().method(Method::PATCH).uri(uri); let request = self.build_request(request, body)?; self.execute(request).await } /// Send a `PUT` request to `route` with optional query parameters, /// returning the body of the response. pub async fn put(&self, route: A, body: Option<&B>) -> Result where A: AsRef, B: Serialize + ?Sized, R: FromResponse, { let response = self ._put(self.parameterized_uri(route, None::<&()>)?, body) .await?; R::from_response(crate::map_github_error(response).await?).await } /// Send a `PATCH` request with no additional post-processing. pub async fn _put( &self, uri: impl TryInto, body: Option<&B>, ) -> Result> { let uri = uri .try_into() .map_err(|_| UriParseError {}) .context(UriParseSnafu)?; let request = Builder::new().method(Method::PUT).uri(uri); let request = self.build_request(request, body)?; self.execute(request).await } pub fn build_request( &self, mut builder: Builder, body: Option<&B>, ) -> Result> { // Since Octocrab doesn't require streamable bodies(aka, file upload) because it is serde::Serialize), // we can just use String body, since it is both http_body::Body(required by Hyper::Client), and Clone(required by BoxService). // In case octocrab needs to support cases where body is strictly streamable, it should use something like reqwest::Body, // since it differentiates between retryable bodies, and streams(aka, it implements try_clone(), which is needed for middlewares like retry). if let Some(body) = body { builder = builder.header(http::header::CONTENT_TYPE, "application/json"); let request = builder .body(serde_json::to_string(body).context(SerdeSnafu)?) .context(HttpSnafu)?; Ok(request) } else { Ok(builder.body(String::new()).context(HttpSnafu)?) } } /// Send a `DELETE` request to `route` with optional query body, /// returning the body of the response. pub async fn delete(&self, route: A, body: Option<&B>) -> Result where A: AsRef, B: Serialize + ?Sized, R: FromResponse, { let response = self ._delete(self.parameterized_uri(route, None::<&()>)?, body) .await?; R::from_response(crate::map_github_error(response).await?).await } /// Send a `DELETE` request with no additional post-processing. pub async fn _delete( &self, uri: impl TryInto, body: Option<&B>, ) -> Result> { let uri = uri .try_into() .map_err(|_| UriParseError {}) .context(UriParseSnafu)?; let request = self.build_request(Builder::new().method(Method::DELETE).uri(uri), body)?; self.execute(request).await } /// Requests a fresh installation auth token and caches it. Returns the token. async fn request_installation_auth_token(&self) -> Result { let (app, installation, token) = if let AuthState::Installation { ref app, installation, ref token, } = self.auth_state { (app, installation, token) } else { panic!("Installation not configured"); }; let mut request = Builder::new(); let mut sensitive_value = HeaderValue::from_str(format!("Bearer {}", app.generate_bearer_token()?).as_str()) .map_err(http::Error::from) .context(HttpSnafu)?; let uri = http::Uri::builder() .path_and_query(format!("/app/installations/{installation}/access_tokens")) .build() .context(HttpSnafu)?; sensitive_value.set_sensitive(true); request = request .header(hyper::header::AUTHORIZATION, sensitive_value) .method(http::Method::POST) .uri(uri); let response = self .send(request.body(String::new()).context(HttpSnafu)?) .await?; let _status = response.status(); let token_object = InstallationToken::from_response(crate::map_github_error(response).await?).await?; let expiration = token_object .expires_at .map(|time| { DateTime::::from_str(&time).map_err(|e| error::Error::Other { source: Box::new(e), backtrace: snafu::Backtrace::generate(), }) }) .transpose()?; #[cfg(feature = "tracing")] tracing::debug!("Token expires at: {:?}", expiration); token.set(token_object.token.clone(), expiration); Ok(SecretString::new(token_object.token)) } /// Send the given request to the underlying service pub async fn send(&self, request: Request) -> Result> { let mut svc = self.client.clone(); let response: Response = svc .ready() .await .context(ServiceSnafu)? .call(request) .await .context(ServiceSnafu)?; Ok(response) //todo: attempt to downcast error to something more specific before returning. (Currently having trouble with this because I am not accustomed with snafu) // map_err(|err| { // // Error decorating request // err.downcast::() // .map(|e| *e) // // Error requesting // .or_else(|err| err.downcast::().map(|err| Error::HyperError(*err))) // // Error from another middleware // .unwrap_or_else(|err| Error::Service(err)) // })?; } /// Execute the given `request` using octocrab's Client. pub async fn execute( &self, request: http::Request, ) -> Result> { let (mut parts, body) = request.into_parts(); // Saved request that we can retry later if necessary let auth_header: Option = match self.auth_state { AuthState::None => None, AuthState::App(ref app) => Some( HeaderValue::from_str(format!("Bearer {}", app.generate_bearer_token()?).as_str()) .map_err(http::Error::from) .context(HttpSnafu)?, ), AuthState::BasicAuth { ref username, ref password, } => { // Equivalent implementation of: https://github.com/seanmonstar/reqwest/blob/df2b3baadc1eade54b1c22415792b778442673a4/src/util.rs#L3-L23 use base64::prelude::BASE64_STANDARD; use base64::write::EncoderWriter; let mut buf = b"Basic ".to_vec(); { let mut encoder = EncoderWriter::new(&mut buf, &BASE64_STANDARD); write!(encoder, "{}:{}", username, password) .expect("writing to a Vec never fails"); } Some(HeaderValue::from_bytes(&buf).expect("base64 is always valid HeaderValue")) } AuthState::Installation { ref token, .. } => { let token = if let Some(token) = token.valid_token() { token } else { self.request_installation_auth_token().await? }; Some( HeaderValue::from_str(format!("Bearer {}", token.expose_secret()).as_str()) .map_err(http::Error::from) .context(HttpSnafu)?, ) } }; if let Some(mut auth_header) = auth_header { auth_header.set_sensitive(true); parts .headers .insert(hyper::header::AUTHORIZATION, auth_header); } let request = http::Request::from_parts(parts, body); let response = self.send(request).await?; let status = response.status(); if StatusCode::UNAUTHORIZED == status { if let AuthState::Installation { ref token, .. } = self.auth_state { token.clear(); } } Ok(response) } pub async fn follow_location_to_data( &self, response: http::Response, ) -> crate::Result> { if let Some(redirect) = response.headers().get(http::header::LOCATION) { let location = redirect.to_str().expect("Location URL not valid str"); self._get(location).await } else { Ok(response) } } } /// # Utility Methods impl Octocrab { /// A convenience method to get a page of results (if present). pub async fn get_page( &self, uri: &Option, ) -> crate::Result>> { match uri { Some(uri) => self.get(uri.to_string(), None::<&()>).await.map(Some), None => Ok(None), } } /// A convenience method to get all the results starting at a given /// page. pub async fn all_pages( &self, mut page: Page, ) -> crate::Result> { let mut ret = page.take_items(); while let Some(mut next_page) = self.get_page(&page.next).await? { ret.append(&mut next_page.take_items()); page = next_page; } Ok(ret) } } #[cfg(test)] mod tests { // tokio runtime seems to be needed for tower: https://users.rust-lang.org/t/no-reactor-running-when-calling-runtime-spawn/81256 #[tokio::test] async fn parametrize_uri_valid() { //Previously, invalid characters were handled by url lib's parse function. //Todo: should we handle encoding of uri routes ourselves? let uri = crate::instance() .parameterized_uri("/help%20world", None::<&()>) .unwrap(); assert_eq!(uri.path(), "/help%20world"); } #[tokio::test] async fn extra_headers() { use http::header::HeaderName; use wiremock::{matchers, Mock, MockServer, ResponseTemplate}; let response = ResponseTemplate::new(304).append_header("etag", "\"abcd\""); let mock_server = MockServer::start().await; Mock::given(matchers::method("GET")) .and(matchers::path_regex(".*")) .and(matchers::header("x-test1", "hello")) .and(matchers::header("x-test2", "goodbye")) .respond_with(response) .expect(1) .mount(&mock_server) .await; crate::OctocrabBuilder::default() .base_uri(mock_server.uri()) .unwrap() .add_header(HeaderName::from_static("x-test1"), "hello".to_string()) .add_header(HeaderName::from_static("x-test2"), "goodbye".to_string()) .build() .unwrap() .repos("XAMPPRocky", "octocrab") .events() .send() .await .unwrap(); } use super::*; use chrono::Duration; #[test] fn clear_token() { let cache = CachedToken(RwLock::new(None)); cache.set("secret".to_string(), None); cache.clear(); assert!(cache.valid_token().is_none(), "Token was not cleared."); } #[test] fn no_token_when_expired() { let cache = CachedToken(RwLock::new(None)); let expiration = Utc::now() + Duration::seconds(9); cache.set("secret".to_string(), Some(expiration)); assert!( cache .valid_token_with_buffer(Duration::seconds(10)) .is_none(), "Token should be considered expired due to buffer." ); } #[test] fn get_valid_token_outside_buffer() { let cache = CachedToken(RwLock::new(None)); let expiration = Utc::now() + Duration::seconds(12); cache.set("secret".to_string(), Some(expiration)); assert!( cache .valid_token_with_buffer(Duration::seconds(10)) .is_some(), "Token should still be valid outside of buffer." ); } #[test] fn get_valid_token_without_expiration() { let cache = CachedToken(RwLock::new(None)); cache.set("secret".to_string(), None); assert!( cache .valid_token_with_buffer(Duration::seconds(10)) .is_some(), "Token with no expiration should always be considered valid." ); } } octocrab-0.31.2/src/models/activity.rs000064400000000000000000000024761046102023000157700ustar 00000000000000use super::*; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[non_exhaustive] pub struct Notification { pub id: NotificationId, pub repository: Repository, pub subject: Subject, pub reason: String, pub unread: bool, pub updated_at: chrono::DateTime, pub last_read_at: Option>, pub url: Url, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] #[non_exhaustive] pub enum Reason { Assign, Author, Comment, Invitation, Manual, Mention, #[serde(rename = "review_requested")] ReviewRequested, #[serde(rename = "security_alert")] SecurityAlert, #[serde(rename = "state_change")] StateChange, Subscribed, #[serde(rename = "team_mention")] TeamMention, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[non_exhaustive] pub struct Subject { pub title: String, pub url: Option, pub latest_comment_url: Option, pub r#type: String, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[non_exhaustive] pub struct ThreadSubscription { pub subscribed: bool, pub ignored: bool, pub reason: Option, pub created_at: chrono::DateTime, pub url: Url, pub thread_url: Url, } octocrab-0.31.2/src/models/apps.rs000064400000000000000000000122141046102023000150660ustar 00000000000000use super::*; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] #[non_exhaustive] pub struct App { pub id: AppId, #[serde(skip_serializing_if = "Option::is_none")] pub slug: Option, pub node_id: String, pub owner: Author, pub name: String, #[serde(skip_serializing_if = "Option::is_none")] pub description: Option, pub external_url: Url, pub html_url: Url, #[serde(skip_serializing_if = "Option::is_none")] pub created_at: Option>, #[serde(skip_serializing_if = "Option::is_none")] pub updated_at: Option>, pub permissions: Permissions, pub events: Vec, #[serde(skip_serializing_if = "Option::is_none")] pub installations_count: Option, #[serde(skip_serializing_if = "Option::is_none")] pub client_id: Option, #[serde(skip_serializing_if = "Option::is_none")] pub client_secret: Option, #[serde(skip_serializing_if = "Option::is_none")] pub webhook_secret: Option, #[serde(skip_serializing_if = "Option::is_none")] pub pem: Option, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[non_exhaustive] pub struct Permissions { #[serde(skip_serializing_if = "Option::is_none")] pub actions: Option, #[serde(skip_serializing_if = "Option::is_none")] pub administration: Option, #[serde(skip_serializing_if = "Option::is_none")] pub blocking: Option, #[serde(skip_serializing_if = "Option::is_none")] pub checks: Option, #[serde(skip_serializing_if = "Option::is_none")] pub contents: Option, #[serde(skip_serializing_if = "Option::is_none")] pub content_references: Option, #[serde(skip_serializing_if = "Option::is_none")] pub deployments: Option, #[serde(skip_serializing_if = "Option::is_none")] pub discussions: Option, #[serde(skip_serializing_if = "Option::is_none")] pub emails: Option, #[serde(skip_serializing_if = "Option::is_none")] pub environments: Option, #[serde(skip_serializing_if = "Option::is_none")] pub followers: Option, #[serde(skip_serializing_if = "Option::is_none")] pub gists: Option, #[serde(skip_serializing_if = "Option::is_none")] pub gpg_keys: Option, #[serde(skip_serializing_if = "Option::is_none")] pub interaction_limits: Option, #[serde(skip_serializing_if = "Option::is_none")] pub issues: Option, #[serde(skip_serializing_if = "Option::is_none")] pub keys: Option, #[serde(skip_serializing_if = "Option::is_none")] pub members: Option, #[serde(skip_serializing_if = "Option::is_none")] pub metadata: Option, #[serde(skip_serializing_if = "Option::is_none")] pub organization_administration: Option, #[serde(skip_serializing_if = "Option::is_none")] pub organization_events: Option, #[serde(skip_serializing_if = "Option::is_none")] pub organization_hooks: Option, #[serde(skip_serializing_if = "Option::is_none")] pub organization_plan: Option, #[serde(skip_serializing_if = "Option::is_none")] pub organization_projects: Option, #[serde(skip_serializing_if = "Option::is_none")] pub organization_secrets: Option, #[serde(skip_serializing_if = "Option::is_none")] pub organization_self_hosted_runners: Option, #[serde(skip_serializing_if = "Option::is_none")] pub organization_user_blocking: Option, #[serde(skip_serializing_if = "Option::is_none")] pub packages: Option, #[serde(skip_serializing_if = "Option::is_none")] pub pages: Option, #[serde(skip_serializing_if = "Option::is_none")] pub plan: Option, #[serde(skip_serializing_if = "Option::is_none")] pub profile: Option, #[serde(skip_serializing_if = "Option::is_none")] pub pull_requests: Option, #[serde(skip_serializing_if = "Option::is_none")] pub repository_hooks: Option, #[serde(skip_serializing_if = "Option::is_none")] pub repository_projects: Option, #[serde(skip_serializing_if = "Option::is_none")] pub secrets: Option, #[serde(skip_serializing_if = "Option::is_none")] pub secret_scanning_alerts: Option, #[serde(skip_serializing_if = "Option::is_none")] pub security_events: Option, #[serde(skip_serializing_if = "Option::is_none")] pub single_file: Option, #[serde(skip_serializing_if = "Option::is_none")] pub starring: Option, #[serde(skip_serializing_if = "Option::is_none")] pub statuses: Option, #[serde(skip_serializing_if = "Option::is_none")] pub team_discussions: Option, #[serde(skip_serializing_if = "Option::is_none")] pub vulnerability_alerts: Option, #[serde(skip_serializing_if = "Option::is_none")] pub watching: Option, #[serde(skip_serializing_if = "Option::is_none")] pub workflows: Option, } octocrab-0.31.2/src/models/checks.rs000064400000000000000000000011651046102023000153660ustar 00000000000000use super::*; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[non_exhaustive] pub struct CheckRun { pub id: CheckRunId, pub node_id: String, pub details_url: Option, pub head_sha: String, pub url: String, pub html_url: Option, pub conclusion: Option, pub started_at: Option>, pub completed_at: Option>, pub name: String, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[non_exhaustive] pub struct ListCheckRuns { pub total_count: u64, pub check_runs: Vec, } octocrab-0.31.2/src/models/commits.rs000064400000000000000000000101331046102023000155740ustar 00000000000000use super::{reactions::ReactionContent, *}; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[non_exhaustive] pub struct Comment { pub html_url: Url, pub url: Url, pub id: CommentId, pub node_id: String, #[serde(skip_serializing_if = "Option::is_none")] pub body: Option, #[serde(skip_serializing_if = "Option::is_none")] pub path: Option, #[serde(skip_serializing_if = "Option::is_none")] pub position: Option, #[serde(skip_serializing_if = "Option::is_none")] pub line: Option, pub commit_id: String, pub user: Author, pub created_at: chrono::DateTime, #[serde(skip_serializing_if = "Option::is_none")] pub updated_at: Option>, pub author_association: AuthorAssociation, #[serde(skip_serializing_if = "Option::is_none")] pub reactions: Option, } /// Reactions summary of a comment #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] #[non_exhaustive] pub struct CommentReactions { pub url: Url, pub total_count: u64, #[serde(flatten)] pub reactions: Option>, } /// Commit Comparison #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CommitComparison { pub ahead_by: i64, /// Commit pub base_commit: Commit, pub behind_by: i64, pub commits: Vec, pub diff_url: String, pub files: Option>, pub html_url: String, /// Commit pub merge_base_commit: Commit, pub patch_url: String, pub permalink_url: String, pub status: GithubCommitStatus, pub total_commits: i64, pub url: String, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CommitElement { pub author: Option, pub comment_count: i64, pub committer: Option, pub message: String, pub tree: Tree, pub url: String, pub verification: Option, } /// Metaproperties for Git author/committer information. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct GitUser { pub date: Option, pub email: Option, pub name: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Tree { pub sha: String, pub url: String, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Verification { pub payload: Option, pub reason: String, pub signature: Option, pub verified: bool, } /// Diff Entry #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum FileStatus { Added, Changed, Copied, Modified, Removed, Renamed, Unchanged, } /// Commit /// Diff Entry #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CommitFile { pub additions: i64, // unlike the schema online, this can be null pub blob_url: Option, pub changes: i64, pub contents_url: String, pub deletions: i64, pub filename: String, pub patch: Option, pub previous_filename: Option, // unlike the schema online, this can be null pub raw_url: Option, pub sha: String, pub status: FileStatus, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CommitParent { pub html_url: Option, pub sha: String, pub url: String, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CommitStats { pub additions: Option, pub deletions: Option, pub total: Option, } /// Commit #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Commit { pub author: Option, pub comments_url: String, pub commit: CommitElement, pub committer: Option, pub files: Option>, pub html_url: String, pub node_id: String, pub parents: Vec, pub sha: String, pub stats: Option, pub url: String, } #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum GithubCommitStatus { Ahead, Behind, Diverged, Identical, } octocrab-0.31.2/src/models/date_serde.rs000064400000000000000000000053001046102023000162200ustar 00000000000000//! Serialization and Deserialization of timestamps in Github API //! //! GitHub API can give (from past experience) either: //! - a seconds timestamp relative to Epoch, or //! - a string containing the timestamp in [RFC 3339](https://datatracker.ietf.org/doc/html/rfc3339#section-5.6) format. //! //! This module handles transparently both formats to deserialize to [`DateTime`](chrono::DateTime). It mostly //! redo things existing in [chrono::serde], because it is otherwise impossible to combine existing `serde_with` modules. use core::fmt; use chrono::{DateTime, LocalResult, TimeZone, Utc}; use serde::{de, Deserialize}; pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> where D: de::Deserializer<'de>, { deserializer.deserialize_any(GithubTimestampVisitor) } /// Helper struct to tell serde the deserializer to use when working with Option> #[derive(Debug, Deserialize)] struct WrappedGithubTimestamp(#[serde(deserialize_with = "deserialize")] DateTime); pub(crate) fn deserialize_opt<'de, D>(deserializer: D) -> Result>, D::Error> where D: de::Deserializer<'de>, { Option::::deserialize(deserializer) .map(|opt_wrapped| opt_wrapped.map(|wrapped| wrapped.0)) } struct GithubTimestampVisitor; impl<'de> de::Visitor<'de> for GithubTimestampVisitor { type Value = DateTime; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter .write_str("a RFC3339 date and time _string_ or a unix timestamp _integer_ in seconds") } fn visit_i64(self, v: i64) -> Result where E: de::Error, { serde_from(Utc.timestamp_opt(v, 0), &v) } fn visit_u64(self, v: u64) -> Result where E: de::Error, { serde_from(Utc.timestamp_opt(v as i64, 0), &v) } fn visit_str(self, v: &str) -> Result where E: de::Error, { v.parse().map_err(E::custom) } } // Convert from chrono local result to something usable by serde // // Ref: https://github.com/chronotope/chrono/blob/bc410bb054932518822eb393147aad939862c7a5/src/naive/datetime/serde.rs#L1051 pub(crate) fn serde_from(me: LocalResult, ts: &V) -> Result where E: de::Error, V: fmt::Display, T: fmt::Display, { match me { LocalResult::None => Err(E::custom(format!("value is not a legal timestamp: {ts}"))), LocalResult::Ambiguous(min, max) => Err(E::custom(format!( "value is an ambiguous timestamp: {ts}. It could be {min} or {max}" ))), LocalResult::Single(val) => Ok(val), } } octocrab-0.31.2/src/models/events/payload/commit_comment.rs000064400000000000000000000016631046102023000221000ustar 00000000000000use crate::models::issues::Comment; use serde::{Deserialize, Serialize}; /// The payload in a [`super::EventPayload::CommitCommentEvent`] type. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[non_exhaustive] pub struct CommitCommentEventPayload { /// The comment this event corresponds to. pub comment: Comment, } #[cfg(test)] mod test { use crate::models::events::{payload::EventPayload, Event}; #[test] fn should_deserialize_with_correct_payload() { let json = include_str!("../../../../tests/resources/commit_comment_event.json"); let event: Event = serde_json::from_str(json).unwrap(); if let Some(EventPayload::CommitCommentEvent(ref payload)) = event.payload.as_ref().unwrap().specific { assert_eq!(payload.comment.id.0, 46377107); } else { panic!("unexpected event payload encountered: {:#?}", event.payload); } } } octocrab-0.31.2/src/models/events/payload/create.rs000064400000000000000000000036541046102023000203330ustar 00000000000000use serde::{Deserialize, Serialize}; /// The payload in a [`super::EventPayload::CreateEvent`] type. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[non_exhaustive] pub struct CreateEventPayload { // a null ref will occur on the initial create event pub r#ref: Option, pub ref_type: String, pub master_branch: String, pub description: Option, pub pusher_type: String, } #[cfg(test)] mod test { use crate::models::events::{payload::EventPayload, Event}; #[test] fn should_deserialize_create_event_with_correct_payload() { let json = include_str!("../../../../tests/resources/create_event.json"); let event: Event = serde_json::from_str(json).unwrap(); assert!(event.payload.is_some()); let payload = event.payload.unwrap().specific.unwrap(); match payload { EventPayload::CreateEvent(payload) => { assert_eq!(payload.r#ref, Some("url-normalisation".to_string())); assert_eq!(payload.ref_type, "branch"); assert_eq!(payload.master_branch, "main"); assert_eq!( payload.description, Some("Your friendly URL vetting service".to_string()) ); assert_eq!(payload.pusher_type, "user"); } _ => panic!("unexpected event deserialized"), } } #[test] fn should_deserialize_null_description_as_none() { let json = include_str!("../../../../tests/resources/create_event_with_null_description.json"); let event: Event = serde_json::from_str(json).unwrap(); assert!(event.payload.is_some()); let payload = event.payload.unwrap().specific.unwrap(); match payload { EventPayload::CreateEvent(payload) => assert_eq!(payload.description, None), _ => panic!("unexpected event deserialized"), } } } octocrab-0.31.2/src/models/events/payload/delete.rs000064400000000000000000000017301046102023000203230ustar 00000000000000use serde::{Deserialize, Serialize}; /// The payload in a [`super::EventPayload::DeleteEvent`] type. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[non_exhaustive] pub struct DeleteEventPayload { /// The ref which was deleted. pub r#ref: String, /// The type of the ref which was deleted. pub ref_type: String, } #[cfg(test)] mod test { use crate::models::events::{payload::EventPayload, Event}; #[test] fn should_deserialize_with_correct_payload() { let json = include_str!("../../../../tests/resources/delete_event.json"); let event: Event = serde_json::from_str(json).unwrap(); if let Some(EventPayload::DeleteEvent(ref payload)) = event.payload.as_ref().unwrap().specific { assert_eq!(payload.r#ref, "test2"); assert_eq!(payload.ref_type, "branch"); } else { panic!("unexpected event payload encountered: {:#?}", event.payload); } } } octocrab-0.31.2/src/models/events/payload/fork.rs000064400000000000000000000015421046102023000200230ustar 00000000000000use crate::models::Repository; use serde::{Deserialize, Serialize}; /// The payload in a [`super::EventPayload::ForkEvent`] type. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[non_exhaustive] pub struct ForkEventPayload { /// The fork. pub forkee: Repository, } #[cfg(test)] mod test { use crate::models::events::{payload::EventPayload, Event}; #[test] fn should_deserialize_with_correct_payload() { let json = include_str!("../../../../tests/resources/fork_event.json"); let event: Event = serde_json::from_str(json).unwrap(); if let Some(EventPayload::ForkEvent(ref payload)) = event.payload.as_ref().unwrap().specific { assert_eq!(payload.forkee.id.0, 334843423); } else { panic!("unexpected event payload encountered: {:#?}", event.payload); } } } octocrab-0.31.2/src/models/events/payload/gollum.rs000064400000000000000000000046251046102023000203660ustar 00000000000000use serde::{Deserialize, Serialize}; use url::Url; /// The payload in a [`super::EventPayload::GollumEvent`] type. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[non_exhaustive] pub struct GollumEventPayload { /// The pages that were updated. pub pages: Vec, } /// A page in a [`GollumEventPayload`]. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[non_exhaustive] pub struct GollumEventPage { /// The name of the page. pub page_name: String, /// The title of the page. pub title: String, /// The action performed on the page. pub action: GollumEventPageAction, /// The latest commit SHA of the page. pub sha: String, /// Url to the page. pub html_url: Url, } /// The action performed on a given page. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] #[non_exhaustive] pub enum GollumEventPageAction { Created, Edited, } #[cfg(test)] mod test { use super::GollumEventPageAction; use crate::models::events::{payload::EventPayload, Event}; #[test] fn should_deserialize_action_from_lowercase() { let actions = vec![ (r#""created""#, GollumEventPageAction::Created), (r#""edited""#, GollumEventPageAction::Edited), ]; for (action_str, action) in actions { let deserialized = serde_json::from_str(action_str).unwrap(); assert_eq!(action, deserialized); } } #[test] fn should_deserialize_with_correct_payload() { let json = include_str!("../../../../tests/resources/gollum_event.json"); let event: Event = serde_json::from_str(json).unwrap(); if let Some(EventPayload::GollumEvent(ref payload)) = event.payload.as_ref().unwrap().specific { assert_eq!(payload.pages[0].page_name, "Home"); assert_eq!(payload.pages[0].title, "Home"); assert_eq!(payload.pages[0].action, GollumEventPageAction::Created); assert_eq!( payload.pages[0].sha, "738b45139cbf06c11f3013e4b2b1a1ad370696ca" ); assert_eq!( payload.pages[0].html_url.to_string(), "https://github.com/wayofthepie/test-events/wiki/Home" ); } else { panic!("unexpected event payload encountered: {:#?}", event.payload); } } } octocrab-0.31.2/src/models/events/payload/installation.rs000064400000000000000000000030651046102023000215650ustar 00000000000000//! This event occurs when there is activity relating to a GitHub App //! installation. All GitHub Apps receive this event by default. You cannot //! manually subscribe to this event. use serde::{Deserialize, Serialize}; use super::InstallationEventRepository; use crate::models::Author; /// The payload in a webhook installation event type. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[non_exhaustive] pub struct InstallationEventPayload { /// The action this event represents. pub action: InstallationEventAction, /// An enterprise on GitHub pub enterprise: Option, /// An array of repositories that the installation can access pub repositories: Vec, /// The initiator of the request, mainly for the [`created`](InstallationAction::Created) action pub requester: Option, } /// The action on an installation this event corresponds to. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] #[non_exhaustive] pub enum InstallationEventAction { /// Someone installed a GitHub App on a user or organization account. Created, /// Someone uninstalled a GitHub App on a user or organization account. Deleted, /// Someone granted new permissions to a GitHub App. NewPermissionsAccepted, /// Someone blocked access by a GitHub App to their user or organization account. Suspend, /// A GitHub App that was blocked from accessing a user or organization account was given access the account again. Unsuspend, } octocrab-0.31.2/src/models/events/payload/installation_repositories.rs000064400000000000000000000035131046102023000243720ustar 00000000000000//! This event occurs when there is activity relating to which repositories a //! GitHub App installation can access. All GitHub Apps receive this event by //! default. You cannot manually subscribe to this event. use serde::{Deserialize, Serialize}; use super::InstallationEventRepository; use crate::models::Author; /// The payload in a webhook installation_repositories event type. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[non_exhaustive] pub struct InstallationRepositoriesEventPayload { /// The action this event represents. pub action: InstallationRepositoriesEventAction, /// An enterprise on GitHub pub enterprise: Option, /// An array of repositories, which were added to the installation pub repositories_added: Vec, /// An array of repositories, which were removed from the installation pub repositories_removed: Vec, /// Describe whether all repositories have been selected or there's a selection involved pub repository_selection: InstallationRepositoriesEventSelection, /// The initiator of the request, mainly for the [`created`](InstallationAction::Created) action pub requester: Option, } /// The action on an installation this event corresponds to. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] #[non_exhaustive] pub enum InstallationRepositoriesEventAction { /// A GitHub App installation was granted access to one or more repositories. Added, /// Access to one or more repositories was revoked for a GitHub App installation. Removed, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] #[non_exhaustive] pub enum InstallationRepositoriesEventSelection { All, Selected, } octocrab-0.31.2/src/models/events/payload/installation_target.rs000064400000000000000000000021031046102023000231230ustar 00000000000000//! This event occurs when there is activity relating to the user or //! organization account that a GitHub App is installed on. use serde::{Deserialize, Serialize}; use crate::models::orgs::Organization; /// The payload in a webhook installation_target event type. /// /// Somebody renamed the user or organization account that a GitHub App is installed on. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[non_exhaustive] pub struct InstallationTargetEventPayload { pub account: Organization, pub changes: InstallationTargetChanges, pub target_type: String, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[non_exhaustive] pub struct InstallationTargetChanges { pub login: InstallationTargetLoginChanges, pub slug: InstallationTargetSlugChanges, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[non_exhaustive] pub struct InstallationTargetLoginChanges { pub from: String, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[non_exhaustive] pub struct InstallationTargetSlugChanges { pub from: String, } octocrab-0.31.2/src/models/events/payload/issue_comment.rs000064400000000000000000000064251046102023000217410ustar 00000000000000use crate::models::issues::{Comment, Issue}; use serde::{Deserialize, Serialize}; /// The payload in a [`super::EventPayload::IssueCommentEvent`] type. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[non_exhaustive] pub struct IssueCommentEventPayload { /// The action this event represents. pub action: IssueCommentEventAction, /// The issue this event corresponds to. pub issue: Issue, /// The comment this event corresponds to. pub comment: Comment, /// The changes to the body of the issue comment if /// this event is of type [`IssueCommentEventAction::Edited`]. pub changes: Option, } /// The change which occurred in an event of type [`IssueCommentEventAction::Edited`]. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] #[non_exhaustive] pub enum IssueCommentEventChanges { Body(IssueCommentEventChangesFrom), } /// The previous value of the body of the issue comment which has changed. Only /// available in an event of type [`IssueCommentEventAction::Edited`]. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[non_exhaustive] pub struct IssueCommentEventChangesFrom { pub from: String, } /// The action on an issue comment this event corresponds to. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] #[non_exhaustive] pub enum IssueCommentEventAction { Created, /// Only available on webhooks. Deleted, /// Only available on webhooks. Edited, } #[cfg(test)] mod test { use super::{IssueCommentEventAction, IssueCommentEventChanges, IssueCommentEventChangesFrom}; use crate::models::events::{payload::EventPayload, Event}; use serde_json::json; #[test] fn should_deserialize_action_from_lowercase() { let actions = vec![ (r#""created""#, IssueCommentEventAction::Created), (r#""edited""#, IssueCommentEventAction::Edited), (r#""deleted""#, IssueCommentEventAction::Deleted), ]; for (action_str, action) in actions { let deserialized = serde_json::from_str(action_str).unwrap(); assert_eq!(action, deserialized); } } #[test] fn should_deserialize_body_changes() { let json = json!({ "body": { "from": "test" } }); let deserialized = serde_json::from_value::(json).unwrap(); assert_eq!( deserialized, IssueCommentEventChanges::Body(IssueCommentEventChangesFrom { from: "test".to_owned() }) ); } #[test] fn should_deserialize_with_correct_payload() { let json = include_str!("../../../../tests/resources/issue_comment_event.json"); let event: Event = serde_json::from_str(json).unwrap(); if let Some(EventPayload::IssueCommentEvent(ref payload)) = event.payload.as_ref().unwrap().specific { assert_eq!(payload.action, IssueCommentEventAction::Created); assert_eq!(payload.issue.id.0, 785981862); assert_eq!(payload.comment.id.0, 760203693); } else { panic!("unexpected event payload encountered: {:#?}", event.payload); } } } octocrab-0.31.2/src/models/events/payload/issues.rs000064400000000000000000000105711046102023000203770ustar 00000000000000use crate::models::{issues::Issue, Author, Label}; use serde::{Deserialize, Serialize}; /// The payload in a [`super::EventPayload::IssuesEvent`] type. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[non_exhaustive] pub struct IssuesEventPayload { /// The action this event represents. pub action: IssuesEventAction, /// The issue this event corresponds to. pub issue: Issue, /// The changes to body or title if this event is of type [`IssuesEventAction::Edited`]. pub changes: Option, /// The optional user who was assigned or unassigned from the issue. /// /// Set when they type is [`IssuesEventAction::Assigned`] or /// [`IssuesEventAction::Unassigned`]. pub assignee: Option, /// The optional label added or removed from the issue. /// /// Set when the type is [`IssuesEventAction::Labeled`] or /// [`IssuesEventAction::Unlabeled`]. pub label: Option