├── .gitignore ├── Cargo.toml ├── releases ├── README.md ├── src │ ├── api.rs │ ├── toml.rs │ ├── json.rs │ ├── lib.rs │ └── main.rs ├── Cargo.toml ├── rustfmt.toml ├── releases.json └── Cargo.lock ├── README.md ├── cargo-rbmt ├── CHANGELOG.md ├── justfile ├── Cargo.toml ├── src │ ├── bench.rs │ ├── docs.rs │ ├── environment.rs │ ├── toolchain.rs │ ├── main.rs │ ├── lock.rs │ ├── integration.rs │ ├── prerelease.rs │ ├── lint.rs │ └── test.rs └── README.md ├── rbmt.toml ├── .github └── workflows │ ├── shellcheck.yml │ └── ci.yml ├── ci ├── README.md └── run_task.sh ├── LICENSE └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "releases", 4 | "cargo-rbmt", 5 | ] 6 | resolver = "2" 7 | -------------------------------------------------------------------------------- /releases/README.md: -------------------------------------------------------------------------------- 1 | Releases Tool 2 | ============= 3 | 4 | This crate provides a tool for doing various release related checks. 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `rust-bitcoin` Maintainer Tools 2 | 3 | This repository contains utilities for maintaining projects in the rust-bitcoin ecosystem. 4 | -------------------------------------------------------------------------------- /releases/src/api.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: CC0-1.0 2 | 3 | //! Interact with the crates.io API. 4 | 5 | pub fn latest_release(package: &str) -> Release { todo!() } 6 | -------------------------------------------------------------------------------- /cargo-rbmt/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [0.1.0] - 4 | 5 | * Initial release of `rust-bitcoin-maintainer-tools` (executable: `rbmt`) matching functionality of shell scripts. 6 | -------------------------------------------------------------------------------- /cargo-rbmt/justfile: -------------------------------------------------------------------------------- 1 | # List available recipes. 2 | _default: 3 | @just --list 4 | 5 | # Run tests. 6 | test: 7 | cargo test 8 | 9 | # Install rbmt from the local path. 10 | install: 11 | cargo install --path . 12 | -------------------------------------------------------------------------------- /rbmt.toml: -------------------------------------------------------------------------------- 1 | [lint] 2 | allowed_duplicates = [ 3 | "windows-sys", 4 | "windows-targets", 5 | "windows_aarch64_gnullvm", 6 | "windows_aarch64_msvc", 7 | "windows_i686_gnu", 8 | "windows_i686_gnullvm", 9 | "windows_i686_msvc", 10 | "windows_x86_64_gnu", 11 | "windows_x86_64_gnullvm", 12 | "windows_x86_64_msvc", 13 | ] 14 | -------------------------------------------------------------------------------- /.github/workflows/shellcheck.yml: -------------------------------------------------------------------------------- 1 | name: Shellcheck 2 | on: 3 | pull_request: 4 | branches: 5 | - master 6 | jobs: 7 | shellcheck: 8 | name: Shellcheck 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v4 12 | - name: Run ShellCheck 13 | uses: ludeeus/action-shellcheck@2.0.0 14 | env: 15 | SHELLCHECK_OPTS: -x # allow outside sources 16 | -------------------------------------------------------------------------------- /cargo-rbmt/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cargo-rbmt" 3 | version = "0.1.0" 4 | authors = ["Nick Johnson "] 5 | license = "CC0-1.0" 6 | edition = "2021" 7 | rust-version = "1.74.0" 8 | 9 | [[bin]] 10 | name = "cargo-rbmt" 11 | path = "src/main.rs" 12 | 13 | [dependencies] 14 | xshell = "0.2" 15 | clap = { version = "4", features = ["derive"] } 16 | serde = { version = "1.0", features = ["derive"] } 17 | serde_json = "1.0" 18 | toml = "0.8" 19 | -------------------------------------------------------------------------------- /releases/src/toml.rs: -------------------------------------------------------------------------------- 1 | //! Types that represent a Rust crate manifest. 2 | // Chronic NIH syndrome. 3 | 4 | use serde::Deserialize; 5 | 6 | #[derive(Deserialize)] 7 | struct Manifest { 8 | package: Package, 9 | dependencies: Dependencies, 10 | } 11 | 12 | #[derive(Deserialize)] 13 | struct Package { 14 | name: String, 15 | version: String, 16 | authors: Vec, 17 | license: String, 18 | repository: String, 19 | description: String, 20 | categories: Vec, 21 | keywords: Vec, 22 | readme: String, 23 | edition: String, 24 | rust_version: String, 25 | } 26 | 27 | #[derive(Deserialize)] 28 | struct Dependencies { 29 | 30 | } 31 | -------------------------------------------------------------------------------- /cargo-rbmt/src/bench.rs: -------------------------------------------------------------------------------- 1 | //! Benchmark testing tasks. 2 | 3 | use crate::environment::{get_crate_dirs, quiet_println}; 4 | use crate::quiet_cmd; 5 | use crate::toolchain::{check_toolchain, Toolchain}; 6 | use xshell::Shell; 7 | 8 | /// Run benchmark tests for all crates in the workspace. 9 | pub fn run(sh: &Shell, packages: &[String]) -> Result<(), Box> { 10 | check_toolchain(sh, Toolchain::Nightly)?; 11 | 12 | let crate_dirs = get_crate_dirs(sh, packages)?; 13 | 14 | quiet_println(&format!( 15 | "Running bench tests for {} crates", 16 | crate_dirs.len() 17 | )); 18 | 19 | for crate_dir in &crate_dirs { 20 | quiet_println(&format!("Running bench tests in: {}", crate_dir)); 21 | 22 | // Use pushd pattern to change and restore directory. 23 | let _dir = sh.push_dir(crate_dir); 24 | 25 | quiet_cmd!(sh, "cargo --locked bench") 26 | .env("RUSTFLAGS", "--cfg=bench") 27 | .run()?; 28 | } 29 | 30 | Ok(()) 31 | } 32 | -------------------------------------------------------------------------------- /releases/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "releases" 3 | version = "0.1.0" 4 | authors = ["Tobin C. Harding "] 5 | license = "CC0-1.0" 6 | repository = "https://github.com/rust-bitcoin/rust-bitcoin-maintainer-tools/" 7 | description = "Tool for checking various release related things." 8 | categories = [] 9 | keywords = [] 10 | readme = "README.md" 11 | edition = "2021" 12 | rust-version = "1.74.0" 13 | 14 | [dependencies] 15 | anyhow = { version = "1.0.82", default-features = false, features = ["std"] } 16 | clap = { version = "4.5.4", default-features = false, features = ["std", "color", "help", "usage", "error-context", "suggestions", "derive", "cargo"] } 17 | crates_io_api = { version = "0.11.0", default-features = false, features = ["rustls"] } 18 | semver = { version = "1.0.22", default-features = false, features = ["std"] } 19 | serde_json = { version = "1.0.116", default-features = false, features = [] } 20 | serde = { version = "1.0.198", features = ["derive"] } 21 | tokio = { version = "1.37.0", features = ["rt-multi-thread", "macros"] } 22 | toml = { version = "0.8.12", default-features = false, features = ["display", "parse"] } 23 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | 8 | jobs: 9 | dogfood: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v6 13 | 14 | - name: Install Rust toolchains 15 | uses: actions-rust-lang/setup-rust-toolchain@v1 16 | with: 17 | toolchain: nightly,1.74.0,stable 18 | components: clippy,rustfmt 19 | 20 | - name: Install cargo-rbmt from current commit 21 | run: cargo install --locked --path cargo-rbmt 22 | 23 | - name: Run lint on workspace 24 | run: cargo +nightly rbmt --lock-file existing lint 25 | 26 | - name: Build documentation (stable) 27 | run: cargo rbmt --lock-file existing docs 28 | 29 | - name: Build documentation (docs.rs / nightly) 30 | run: cargo +nightly rbmt --lock-file existing docsrs 31 | 32 | - name: Run tests with stable 33 | run: cargo rbmt --lock-file existing test stable 34 | 35 | - name: Run tests with nightly 36 | run: cargo +nightly rbmt --lock-file existing test nightly 37 | 38 | - name: Run tests with MSRV 39 | run: cargo +1.74.0 rbmt --lock-file existing test msrv 40 | -------------------------------------------------------------------------------- /releases/src/json.rs: -------------------------------------------------------------------------------- 1 | //! Types that represent the JSON schema and provide deserialization. 2 | //! 3 | //! We deserialize to Rust std types then convert to more strictly typed types manually. 4 | 5 | #![allow(dead_code)] // This is just the JSON schemar, all fields exist. 6 | 7 | use serde::Deserialize; 8 | 9 | /// The `releases.json` config file. 10 | #[derive(Debug, Deserialize)] 11 | pub struct Config { 12 | /// The github organisation this config file relates to. 13 | pub org: String, 14 | /// List of the latest releases. 15 | pub latests: Vec, 16 | /// List of all releases we run checks against. 17 | pub releases: Vec, 18 | } 19 | 20 | /// As specific version of a crate. 21 | #[derive(Debug, Deserialize)] 22 | pub struct CrateVersion { 23 | /// The crate's package name on crates.io 24 | pub package: String, 25 | /// The dependencies semantic version number. 26 | pub version: String, 27 | } 28 | 29 | /// A version of one of the crates along with a list of its dependencies (and their versions) - used 30 | /// to make a dependency graph. 31 | #[derive(Debug, Deserialize)] 32 | pub struct CrateNode { 33 | /// The crate's package name on crates.io 34 | pub package: String, 35 | /// The release's semantic version number. 36 | pub version: String, 37 | /// List of this releases dependencies. 38 | pub dependencies: Vec, 39 | } 40 | -------------------------------------------------------------------------------- /cargo-rbmt/src/docs.rs: -------------------------------------------------------------------------------- 1 | //! Documentation building tasks. 2 | 3 | use crate::quiet_cmd; 4 | use crate::toolchain::{check_toolchain, Toolchain}; 5 | use xshell::Shell; 6 | 7 | /// Build documentation for end users with the stable toolchain. 8 | /// 9 | /// This verifies that `cargo doc` works correctly for users with stable Rust. 10 | /// Uses basic rustdoc warnings to catch common documentation issues. 11 | pub fn run(sh: &Shell, packages: &[String]) -> Result<(), Box> { 12 | check_toolchain(sh, Toolchain::Stable)?; 13 | 14 | let mut cmd = quiet_cmd!(sh, "cargo --locked doc --all-features --no-deps"); 15 | 16 | // Add package filters if specified. 17 | for package in packages { 18 | cmd = cmd.args(&["-p", package]); 19 | } 20 | 21 | cmd.env("RUSTDOCFLAGS", "-D warnings").run()?; 22 | 23 | Ok(()) 24 | } 25 | 26 | /// Build documentation for docs.rs with the nightly toolchain. 27 | /// 28 | /// This emulates the docs.rs build environment by using the nightly toolchain 29 | /// with `--cfg docsrs` enabled. This catches docs.rs-specific issues. 30 | pub fn run_docsrs(sh: &Shell, packages: &[String]) -> Result<(), Box> { 31 | check_toolchain(sh, Toolchain::Nightly)?; 32 | 33 | let mut cmd = quiet_cmd!(sh, "cargo --locked doc --all-features --no-deps"); 34 | 35 | // Add package filters if specified. 36 | for package in packages { 37 | cmd = cmd.args(&["-p", package]); 38 | } 39 | 40 | cmd.env( 41 | "RUSTDOCFLAGS", 42 | "--cfg docsrs -D warnings -D rustdoc::broken-intra-doc-links", 43 | ) 44 | .run()?; 45 | 46 | Ok(()) 47 | } 48 | -------------------------------------------------------------------------------- /releases/rustfmt.toml: -------------------------------------------------------------------------------- 1 | ignore = [] 2 | hard_tabs = false 3 | tab_spaces = 4 4 | newline_style = "Auto" 5 | indent_style = "Block" 6 | 7 | max_width = 100 # This is number of characters. 8 | # `use_small_heuristics` is ignored if the granular width config values are explicitly set. 9 | use_small_heuristics = "Max" # "Max" == All granular width settings same as `max_width`. 10 | # # Granular width configuration settings. These are percentages of `max_width`. 11 | # fn_call_width = 60 12 | # attr_fn_like_width = 70 13 | # struct_lit_width = 18 14 | # struct_variant_width = 35 15 | # array_width = 60 16 | # chain_width = 60 17 | # single_line_if_else_max_width = 50 18 | 19 | wrap_comments = false 20 | format_code_in_doc_comments = false 21 | comment_width = 100 # Default 80 22 | normalize_comments = false 23 | normalize_doc_attributes = false 24 | format_strings = false 25 | format_macro_matchers = false 26 | format_macro_bodies = true 27 | hex_literal_case = "Preserve" 28 | empty_item_single_line = true 29 | struct_lit_single_line = true 30 | fn_single_line = true # Default false 31 | where_single_line = false 32 | imports_indent = "Block" 33 | imports_layout = "Mixed" 34 | imports_granularity = "Module" # Default "Preserve" 35 | group_imports = "StdExternalCrate" # Default "Preserve" 36 | reorder_imports = true 37 | reorder_modules = true 38 | reorder_impl_items = false 39 | type_punctuation_density = "Wide" 40 | space_before_colon = false 41 | space_after_colon = true 42 | spaces_around_ranges = false 43 | binop_separator = "Front" 44 | remove_nested_parens = true 45 | combine_control_expr = true 46 | overflow_delimited_expr = false 47 | struct_field_align_threshold = 0 48 | enum_discrim_align_threshold = 0 49 | match_arm_blocks = false # Default true 50 | match_arm_leading_pipes = "Never" 51 | force_multiline_blocks = false 52 | fn_params_layout = "Tall" 53 | brace_style = "SameLineWhere" 54 | control_brace_style = "AlwaysSameLine" 55 | trailing_semicolon = true 56 | trailing_comma = "Vertical" 57 | match_block_trailing_comma = false 58 | blank_lines_upper_bound = 1 59 | blank_lines_lower_bound = 0 60 | edition = "2021" 61 | version = "One" 62 | inline_attribute_width = 0 63 | format_generated_files = true 64 | merge_derives = true 65 | use_try_shorthand = false 66 | use_field_init_shorthand = false 67 | force_explicit_abi = true 68 | condense_wildcard_suffixes = false 69 | color = "Auto" 70 | unstable_features = false 71 | disable_all_formatting = false 72 | skip_children = false 73 | hide_parse_errors = false 74 | error_on_line_overflow = false 75 | error_on_unformatted = false 76 | emit_mode = "Files" 77 | make_backup = false 78 | -------------------------------------------------------------------------------- /releases/src/lib.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: CC0-1.0 2 | 3 | //! Tool to check various release related things. 4 | 5 | // Coding conventions. 6 | #![warn(missing_docs)] 7 | 8 | use std::fmt; 9 | 10 | use anyhow::bail; 11 | use semver::Version; 12 | 13 | pub mod json; 14 | 15 | /// The state of the rust-bitcoin org that this tool aims to run checks on. 16 | #[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] 17 | pub struct Config { 18 | /// List of the latest releases. 19 | pub latests: Vec, 20 | /// The releases we want to check. 21 | pub releases: Vec, 22 | } 23 | 24 | impl Config { 25 | /// Returns the latest release of the given crate. 26 | pub fn latest(&self, package: &str) -> anyhow::Result { 27 | let mut want = None; 28 | for latest in self.latests.iter() { 29 | if latest.package == package { 30 | want = Some(latest.version.clone()); 31 | } 32 | } 33 | if want.is_none() { 34 | bail!("package {} is not listed in latest section of config file", package); 35 | } 36 | 37 | let mut found = Version::parse("0.0.0").expect("valid zero version"); 38 | let mut release = None; 39 | for r in self.releases.iter() { 40 | if r.package == package && r.version > found { 41 | found = r.version.clone(); 42 | release = Some(r); 43 | } 44 | } 45 | match release { 46 | Some(r) => { 47 | if r.version != want.expect("checked above") { 48 | bail!("the latest version in the releases section for {} does not match the verison in the latest section", package); 49 | } 50 | Ok(r.version.clone()) 51 | } 52 | None => bail!("we don't have a release in the config file for {}", package), 53 | } 54 | } 55 | } 56 | 57 | impl TryFrom for Config { 58 | type Error = semver::Error; 59 | 60 | fn try_from(json: json::Config) -> Result { 61 | let latests: Result, _> = json.latests.into_iter().map(TryFrom::try_from).collect(); 62 | let releases: Result, _> = 63 | json.releases.into_iter().map(TryFrom::try_from).collect(); 64 | Ok(Self { latests: latests?, releases: releases? }) 65 | } 66 | } 67 | 68 | /// As specific version of a crate. 69 | #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 70 | pub struct CrateVersion { 71 | /// The crate's package name on crates.io 72 | pub package: String, 73 | /// The dependencies semantic version number. 74 | pub version: Version, 75 | } 76 | 77 | impl TryFrom for CrateVersion { 78 | type Error = semver::Error; 79 | 80 | fn try_from(json: json::CrateVersion) -> Result { 81 | Ok(Self { package: json.package, version: Version::parse(&json.version)? }) 82 | } 83 | } 84 | 85 | /// A version of one of the crates that lives in the github.com/rust-bitcoin org. 86 | #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 87 | pub struct CrateNode { 88 | /// The crate this release is for. 89 | pub package: String, 90 | /// The release's semantic version number. 91 | pub version: Version, 92 | /// List of this releases dependencies. 93 | pub dependencies: Vec, 94 | } 95 | 96 | impl fmt::Display for CrateNode { 97 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 98 | write!(f, "{} {}", self.package, self.version) 99 | } 100 | } 101 | 102 | impl TryFrom for CrateNode { 103 | type Error = semver::Error; 104 | 105 | fn try_from(json: json::CrateNode) -> Result { 106 | let mut dependencies = vec![]; 107 | for d in json.dependencies { 108 | let converted = CrateVersion::try_from(d)?; 109 | dependencies.push(converted); 110 | } 111 | 112 | Ok(Self { package: json.package, version: Version::parse(&json.version)?, dependencies }) 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /cargo-rbmt/src/environment.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use xshell::Shell; 3 | 4 | /// Environment variable to control output verbosity. 5 | /// Set to "quiet" to suppress informational messages and reduce cargo output. 6 | /// Any other value (or unset) defaults to verbose mode. 7 | const LOG_LEVEL_ENV_VAR: &str = "RBMT_LOG_LEVEL"; 8 | 9 | /// Path to the RBMT configuration file relative to workspace/crate root. 10 | pub const CONFIG_FILE_PATH: &str = "rbmt.toml"; 11 | 12 | /// Check if we're in quiet mode via environment variable. 13 | pub fn is_quiet_mode() -> bool { 14 | env::var(LOG_LEVEL_ENV_VAR).is_ok_and(|v| v == "quiet") 15 | } 16 | 17 | /// Helper macro to create commands that respect quiet mode. 18 | #[macro_export] 19 | macro_rules! quiet_cmd { 20 | ($sh:expr, $($arg:tt)*) => {{ 21 | let mut cmd = xshell::cmd!($sh, $($arg)*); 22 | if $crate::environment::is_quiet_mode() { 23 | cmd = cmd.quiet(); 24 | } 25 | cmd 26 | }}; 27 | } 28 | 29 | /// Print a message unless in quiet mode. 30 | pub fn quiet_println(msg: &str) { 31 | if !is_quiet_mode() { 32 | println!("{}", msg); 33 | } 34 | } 35 | 36 | /// Configure shell log level and output verbosity. 37 | /// Sets cargo output verbosity based on LOG_LEVEL_ENV_VAR. 38 | pub fn configure_log_level(sh: &Shell) { 39 | if is_quiet_mode() { 40 | sh.set_var("CARGO_TERM_VERBOSE", "false"); 41 | sh.set_var("CARGO_TERM_QUIET", "true"); 42 | } else { 43 | sh.set_var("CARGO_TERM_VERBOSE", "true"); 44 | sh.set_var("CARGO_TERM_QUIET", "false"); 45 | } 46 | } 47 | 48 | /// Change to the repository root directory. 49 | /// 50 | /// # Panics 51 | /// 52 | /// Panics if not in a git repository or git command fails. 53 | pub fn change_to_repo_root(sh: &Shell) { 54 | let repo_dir = quiet_cmd!(sh, "git rev-parse --show-toplevel") 55 | .read() 56 | .expect("Failed to get repository root, ensure you're in a git repository"); 57 | sh.change_dir(&repo_dir); 58 | } 59 | 60 | /// Get list of crate directories in the workspace using cargo metadata. 61 | /// Returns fully qualified paths to support various workspace layouts including nested crates. 62 | /// 63 | /// # Arguments 64 | /// 65 | /// * `packages` - Optional filter for specific package names. If empty, returns all packages. 66 | pub fn get_crate_dirs( 67 | sh: &Shell, 68 | packages: &[String], 69 | ) -> Result, Box> { 70 | let metadata = quiet_cmd!(sh, "cargo metadata --no-deps --format-version 1").read()?; 71 | let json: serde_json::Value = serde_json::from_str(&metadata)?; 72 | 73 | let crate_dirs: Vec = json["packages"] 74 | .as_array() 75 | .ok_or("Missing 'packages' field in cargo metadata")? 76 | .iter() 77 | .filter_map(|package| { 78 | let manifest_path = package["manifest_path"].as_str()?; 79 | // Extract directory path from the manifest path, 80 | // e.g., "/path/to/repo/releases/Cargo.toml" -> "/path/to/repo/releases". 81 | let dir_path = manifest_path.trim_end_matches("/Cargo.toml"); 82 | 83 | // Filter by package name if specified. 84 | if !packages.is_empty() { 85 | let package_name = package["name"].as_str()?; 86 | if !packages.contains(&package_name.to_string()) { 87 | return None; 88 | } 89 | } 90 | 91 | Some(dir_path.to_string()) 92 | }) 93 | .collect(); 94 | 95 | Ok(crate_dirs) 96 | } 97 | 98 | /// Get the cargo target directory from metadata. 99 | /// 100 | /// This respects CARGO_TARGET_DIR, .cargo/config.toml, and other cargo 101 | /// target directory configuration. 102 | pub fn get_target_directory(sh: &Shell) -> Result> { 103 | let metadata = quiet_cmd!(sh, "cargo metadata --no-deps --format-version 1").read()?; 104 | let json: serde_json::Value = serde_json::from_str(&metadata)?; 105 | 106 | let target_dir = json["target_directory"] 107 | .as_str() 108 | .ok_or("Missing target_directory in cargo metadata")?; 109 | 110 | Ok(target_dir.to_string()) 111 | } 112 | -------------------------------------------------------------------------------- /cargo-rbmt/src/toolchain.rs: -------------------------------------------------------------------------------- 1 | use std::path::Path; 2 | use xshell::{cmd, Shell}; 3 | 4 | /// Toolchain requirement for a task. 5 | #[derive(Debug, Clone, Copy, PartialEq, Eq, clap::ValueEnum)] 6 | pub enum Toolchain { 7 | /// Nightly toolchain. 8 | Nightly, 9 | /// Stable toolchain. 10 | Stable, 11 | /// Minimum Supported Rust Version. 12 | Msrv, 13 | } 14 | 15 | /// Check if the current toolchain matches the requirement of current crate. 16 | /// 17 | /// # Errors 18 | /// 19 | /// * Cannot determine current toolchain version. 20 | /// * Current toolchain doesn't match requirement. 21 | /// * For MSRV: cannot read rust-version from Cargo.toml. 22 | pub fn check_toolchain(sh: &Shell, required: Toolchain) -> Result<(), Box> { 23 | let current = cmd!(sh, "rustc --version").read()?; 24 | 25 | match required { 26 | Toolchain::Nightly => { 27 | if !current.contains("nightly") { 28 | return Err(format!("Need a nightly compiler; have {}", current).into()); 29 | } 30 | } 31 | Toolchain::Stable => { 32 | if current.contains("nightly") || current.contains("beta") { 33 | return Err(format!("Need a stable compiler; have {}", current).into()); 34 | } 35 | } 36 | Toolchain::Msrv => { 37 | let manifest_path = sh.current_dir().join("Cargo.toml"); 38 | 39 | if !manifest_path.exists() { 40 | return Err("Not in a crate directory (no Cargo.toml found)".into()); 41 | } 42 | 43 | let msrv_version = get_msrv_from_manifest(&manifest_path)?; 44 | let current_version = 45 | extract_version(¤t).ok_or("Could not parse rustc version")?; 46 | 47 | if current_version != msrv_version { 48 | return Err(format!( 49 | "Need Rust {} for MSRV testing in {}; have {}", 50 | msrv_version, 51 | manifest_path.display(), 52 | current_version 53 | ) 54 | .into()); 55 | } 56 | } 57 | } 58 | 59 | Ok(()) 60 | } 61 | 62 | /// Extract MSRV from Cargo.toml using cargo metadata. 63 | fn get_msrv_from_manifest(manifest_path: &Path) -> Result> { 64 | let sh = Shell::new()?; 65 | let metadata = cmd!(sh, "cargo metadata --format-version 1 --no-deps").read()?; 66 | let data: serde_json::Value = serde_json::from_str(&metadata)?; 67 | 68 | // Convert Path to string for comparison. If path contains invalid UTF-8, fail early. 69 | let manifest_path_str = manifest_path.to_str().ok_or_else(|| { 70 | format!( 71 | "Manifest path contains invalid UTF-8: {}", 72 | manifest_path.display() 73 | ) 74 | })?; 75 | 76 | let msrv = data["packages"] 77 | .as_array() 78 | .and_then(|packages| { 79 | packages 80 | .iter() 81 | .find(|pkg| pkg["manifest_path"].as_str() == Some(manifest_path_str)) 82 | }) 83 | .and_then(|pkg| pkg["rust_version"].as_str()) 84 | .ok_or_else(|| { 85 | format!( 86 | "No MSRV (rust-version) specified in {}", 87 | manifest_path.display() 88 | ) 89 | })?; 90 | 91 | Ok(msrv.to_string()) 92 | } 93 | 94 | /// Extract version number from rustc --version output. 95 | /// 96 | /// # Examples 97 | /// 98 | /// `"rustc 1.74.0 (79e9716c9 2023-11-13)"` -> `Some("1.74.0")` 99 | fn extract_version(rustc_version: &str) -> Option<&str> { 100 | rustc_version.split_whitespace().find_map(|part| { 101 | // Split off any suffix like "-nightly" or "-beta". 102 | let version_part = part.split('-').next()?; 103 | 104 | // Version format: digit.digit.digit 105 | let parts: Vec<&str> = version_part.split('.').collect(); 106 | if parts.len() == 3 && parts.iter().all(|p| p.chars().all(|c| c.is_ascii_digit())) { 107 | Some(version_part) 108 | } else { 109 | None 110 | } 111 | }) 112 | } 113 | 114 | #[cfg(test)] 115 | mod tests { 116 | use super::*; 117 | 118 | #[test] 119 | fn test_extract_version() { 120 | assert_eq!( 121 | extract_version("rustc 1.74.0 (79e9716c9 2023-11-13)"), 122 | Some("1.74.0") 123 | ); 124 | assert_eq!( 125 | extract_version("rustc 1.75.0-nightly (12345abcd 2023-11-20)"), 126 | Some("1.75.0") 127 | ); 128 | assert_eq!(extract_version("rustc 1.74.0"), Some("1.74.0")); 129 | assert_eq!(extract_version("rustc unknown version"), None); 130 | assert_eq!(extract_version("no version here"), None); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /cargo-rbmt/src/main.rs: -------------------------------------------------------------------------------- 1 | mod bench; 2 | mod docs; 3 | mod environment; 4 | mod integration; 5 | mod lint; 6 | mod lock; 7 | mod prerelease; 8 | mod test; 9 | mod toolchain; 10 | 11 | use clap::{Parser, Subcommand}; 12 | use std::process; 13 | use xshell::Shell; 14 | 15 | use environment::{change_to_repo_root, configure_log_level}; 16 | use lock::LockFile; 17 | use toolchain::Toolchain; 18 | 19 | #[derive(Parser)] 20 | #[command(name = "cargo-rbmt")] 21 | #[command(about = "Rust Bitcoin Maintainer Tools", long_about = None)] 22 | struct Cli { 23 | /// Lock file to use for dependencies (defaults to recent). 24 | #[arg(long, global = true, value_enum)] 25 | lock_file: Option, 26 | 27 | /// Filter to specific package (can be specified multiple times). 28 | #[arg(short = 'p', long = "package", global = true)] 29 | packages: Vec, 30 | 31 | #[command(subcommand)] 32 | command: Commands, 33 | } 34 | 35 | #[derive(Subcommand)] 36 | enum Commands { 37 | /// Run the linter (clippy) for workspace and all crates. 38 | Lint, 39 | /// Build documentation with stable toolchain. 40 | Docs, 41 | /// Build documentation with nightly toolchain for docs.rs. 42 | Docsrs, 43 | /// Run benchmark tests for all crates. 44 | Bench, 45 | /// Run tests with specified toolchain. 46 | Test { 47 | /// Toolchain to use: stable, nightly, or msrv. 48 | #[arg(value_enum)] 49 | toolchain: Toolchain, 50 | /// Disable debug assertions in compiled code. 51 | #[arg(long)] 52 | no_debug_assertions: bool, 53 | }, 54 | /// Run bitcoin core integration tests. 55 | Integration, 56 | /// Update Cargo-minimal.lock and Cargo-recent.lock files. 57 | Lock, 58 | /// Run pre-release readiness checks. 59 | Prerelease, 60 | } 61 | 62 | fn main() { 63 | // Cargo automatically adds the subcommand name as an extra argument. 64 | // `cargo rbmt test` becomes `cargo-rbmt rbmt test`, so filter it out. 65 | let args = std::env::args() 66 | .enumerate() 67 | .filter(|(i, arg)| !(*i == 1 && arg == "rbmt")) 68 | .map(|(_, arg)| arg); 69 | 70 | let cli = Cli::parse_from(args); 71 | 72 | let sh = Shell::new().unwrap(); 73 | configure_log_level(&sh); 74 | change_to_repo_root(&sh); 75 | 76 | // Restore the specified lock file before running any command (except Lock and Integration). 77 | // Integration tests use their own lock files in the integration package directory. 78 | if let Some(lock_file) = cli.lock_file { 79 | if !matches!(cli.command, Commands::Lock | Commands::Integration) { 80 | if let Err(e) = lock::restore_lock_file(&sh, lock_file) { 81 | eprintln!("Error restoring lock file: {}", e); 82 | process::exit(1); 83 | } 84 | } 85 | } 86 | 87 | match cli.command { 88 | Commands::Lint => { 89 | if let Err(e) = lint::run(&sh, &cli.packages) { 90 | eprintln!("Error running lint task: {}", e); 91 | process::exit(1); 92 | } 93 | } 94 | Commands::Docs => { 95 | if let Err(e) = docs::run(&sh, &cli.packages) { 96 | eprintln!("Error building docs: {}", e); 97 | process::exit(1); 98 | } 99 | } 100 | Commands::Docsrs => { 101 | if let Err(e) = docs::run_docsrs(&sh, &cli.packages) { 102 | eprintln!("Error building docs.rs docs: {}", e); 103 | process::exit(1); 104 | } 105 | } 106 | Commands::Bench => { 107 | if let Err(e) = bench::run(&sh, &cli.packages) { 108 | eprintln!("Error running bench tests: {}", e); 109 | process::exit(1); 110 | } 111 | } 112 | Commands::Test { 113 | toolchain, 114 | no_debug_assertions, 115 | } => { 116 | if let Err(e) = test::run(&sh, toolchain, no_debug_assertions, &cli.packages) { 117 | eprintln!("Error running tests: {}", e); 118 | process::exit(1); 119 | } 120 | } 121 | Commands::Integration => { 122 | if let Err(e) = integration::run(&sh, &cli.packages) { 123 | eprintln!("Error running integration tests: {}", e); 124 | process::exit(1); 125 | } 126 | } 127 | Commands::Lock => { 128 | if let Err(e) = lock::run(&sh) { 129 | eprintln!("Error updating lock files: {}", e); 130 | process::exit(1); 131 | } 132 | } 133 | Commands::Prerelease => { 134 | if let Err(e) = prerelease::run(&sh, &cli.packages) { 135 | eprintln!("Error running pre-release checks: {}", e); 136 | process::exit(1); 137 | } 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /cargo-rbmt/src/lock.rs: -------------------------------------------------------------------------------- 1 | //! Manage cargo lock files for minimal and recent dependency versions. 2 | //! 3 | //! Note: These commands intentionally omit `--locked` because they need to 4 | //! generate and modify lockfiles. Using `--locked` would prevent the dependency 5 | //! resolution we need here. 6 | 7 | use crate::environment::quiet_println; 8 | use crate::quiet_cmd; 9 | use crate::toolchain::{check_toolchain, Toolchain}; 10 | use clap::ValueEnum; 11 | use std::fs; 12 | use xshell::Shell; 13 | 14 | /// The standard Cargo lockfile name. 15 | const CARGO_LOCK: &str = "Cargo.lock"; 16 | 17 | /// Represents the different types of managed lock files. 18 | #[derive(Debug, Clone, Copy, ValueEnum, Default)] 19 | pub enum LockFile { 20 | /// Uses minimal versions that satisfy dependency constraints. 21 | Minimal, 22 | /// Uses recent/updated versions of dependencies. 23 | #[default] 24 | Recent, 25 | /// Uses the existing Cargo.lock as-is (for binary crates). 26 | Existing, 27 | } 28 | 29 | impl LockFile { 30 | /// Get the filename for this lock file type. 31 | pub fn filename(&self) -> &'static str { 32 | match self { 33 | LockFile::Minimal => "Cargo-minimal.lock", 34 | LockFile::Recent => "Cargo-recent.lock", 35 | LockFile::Existing => CARGO_LOCK, 36 | } 37 | } 38 | } 39 | 40 | /// Update Cargo-minimal.lock and Cargo-recent.lock files. 41 | /// 42 | /// * `Cargo-minimal.lock` - Uses minimal versions that satisfy dependency constraints. 43 | /// * `Cargo-recent.lock` - Uses recent/updated versions of dependencies. 44 | /// 45 | /// The minimal versions strategy uses a combination of `-Z direct-minimal-versions` 46 | /// and `-Z minimal-versions` to ensure two rules. 47 | /// 48 | /// 1. Direct dependency versions in manifests are accurate (not bumped by transitive deps). 49 | /// 2. The entire dependency tree uses minimal versions that still satisfy constraints. 50 | /// 51 | /// This helps catch cases where you've specified a minimum version that's too high, 52 | /// or where your code relies on features from newer versions than declared. 53 | pub fn run(sh: &Shell) -> Result<(), Box> { 54 | check_toolchain(sh, Toolchain::Nightly)?; 55 | 56 | let repo_dir = sh.current_dir(); 57 | quiet_println(&format!("Updating lock files in: {}", repo_dir.display())); 58 | 59 | // The `direct-minimal-versions` and `minimal-versions` dependency resolution strategy 60 | // flags each have a little quirk. `direct-minimal-versions` allows transitive versions 61 | // to upgrade, so we are not testing against the actual minimum tree. `minimal-versions` 62 | // allows the direct dependency versions to resolve upward due to transitive requirements, 63 | // so we are not testing the manifest's versions. Combo'd together though, we can get the 64 | // best of both worlds to ensure the actual minimum dependencies listed in the crate 65 | // manifests build. 66 | 67 | // Check that all explicit direct dependency versions are not lying, 68 | // as in, they are not being bumped up by transitive dependency constraints. 69 | quiet_println("Checking direct minimal versions..."); 70 | remove_lock_file(sh)?; 71 | quiet_cmd!(sh, "cargo check --all-features -Z direct-minimal-versions").run()?; 72 | 73 | // Now that our own direct dependency versions can be trusted, check 74 | // against the lowest versions of the dependency tree which still 75 | // satisfy constraints. Use this as the minimal version lock file. 76 | quiet_println("Generating Cargo-minimal.lock..."); 77 | remove_lock_file(sh)?; 78 | quiet_cmd!(sh, "cargo check --all-features -Z minimal-versions").run()?; 79 | copy_lock_file(sh, LockFile::Minimal)?; 80 | 81 | // Conservatively bump of recent dependencies. 82 | quiet_println("Updating Cargo-recent.lock..."); 83 | restore_lock_file(sh, LockFile::Recent)?; 84 | quiet_cmd!(sh, "cargo check --all-features").run()?; 85 | copy_lock_file(sh, LockFile::Recent)?; 86 | 87 | quiet_println("Lock files updated successfully"); 88 | 89 | Ok(()) 90 | } 91 | 92 | /// Remove Cargo.lock file if it exists. 93 | fn remove_lock_file(sh: &Shell) -> Result<(), Box> { 94 | let lock_path = sh.current_dir().join(CARGO_LOCK); 95 | if lock_path.exists() { 96 | fs::remove_file(&lock_path)?; 97 | } 98 | Ok(()) 99 | } 100 | 101 | /// Copy Cargo.lock to a specific lock file. 102 | fn copy_lock_file(sh: &Shell, target: LockFile) -> Result<(), Box> { 103 | let source = sh.current_dir().join(CARGO_LOCK); 104 | let dest = sh.current_dir().join(target.filename()); 105 | fs::copy(&source, &dest)?; 106 | Ok(()) 107 | } 108 | 109 | /// Restore a specific lock file to Cargo.lock. 110 | pub fn restore_lock_file(sh: &Shell, source: LockFile) -> Result<(), Box> { 111 | // Existing uses Cargo.lock as-is, no need to restore. 112 | if matches!(source, LockFile::Existing) { 113 | return Ok(()); 114 | } 115 | 116 | let src_path = sh.current_dir().join(source.filename()); 117 | let dest_path = sh.current_dir().join(CARGO_LOCK); 118 | fs::copy(&src_path, &dest_path)?; 119 | Ok(()) 120 | } 121 | -------------------------------------------------------------------------------- /cargo-rbmt/src/integration.rs: -------------------------------------------------------------------------------- 1 | //! Integration test tasks for packages with bitcoind-tests or similar test packages. 2 | 3 | use crate::environment::{get_crate_dirs, quiet_println, CONFIG_FILE_PATH}; 4 | use crate::quiet_cmd; 5 | use serde::Deserialize; 6 | use std::path::{Path, PathBuf}; 7 | use xshell::{cmd, Shell}; 8 | 9 | /// Integration test configuration loaded from rbmt.toml. 10 | #[derive(Debug, Deserialize, Default)] 11 | #[serde(default)] 12 | struct Config { 13 | integration: IntegrationConfig, 14 | } 15 | 16 | /// Integration-specific configuration. 17 | #[derive(Debug, Deserialize, Default)] 18 | #[serde(default)] 19 | struct IntegrationConfig { 20 | /// Package name containing integration tests (defaults to "bitcoind-tests"). 21 | package: Option, 22 | 23 | /// Bitcoind versions to test (runs each individually). 24 | /// If not specified, discovers all version features from Cargo.toml. 25 | /// 26 | /// # Examples 27 | /// 28 | /// `["29_0", "28_2", "27_2"]` 29 | versions: Option>, 30 | } 31 | 32 | impl IntegrationConfig { 33 | /// Load integration configuration from a crate directory. 34 | fn load(crate_dir: &Path) -> Result> { 35 | let config_path = crate_dir.join(CONFIG_FILE_PATH); 36 | 37 | if !config_path.exists() { 38 | return Ok(IntegrationConfig::default()); 39 | } 40 | 41 | let contents = std::fs::read_to_string(&config_path)?; 42 | let config: Config = toml::from_str(&contents)?; 43 | Ok(config.integration) 44 | } 45 | 46 | /// Get the package name (defaults to "bitcoind-tests"). 47 | fn package_name(&self) -> &str { 48 | self.package.as_deref().unwrap_or("bitcoind-tests") 49 | } 50 | } 51 | 52 | /// Run integration tests for all crates with integration test packages. 53 | /// 54 | /// # Arguments 55 | /// 56 | /// * `packages` - Optional filter for specific package names. 57 | pub fn run(sh: &Shell, packages: &[String]) -> Result<(), Box> { 58 | let crate_dirs = get_crate_dirs(sh, packages)?; 59 | quiet_println(&format!( 60 | "Looking for integration tests in {} crate(s)", 61 | crate_dirs.len() 62 | )); 63 | 64 | for crate_dir in &crate_dirs { 65 | let config = IntegrationConfig::load(Path::new(crate_dir))?; 66 | let integration_dir = PathBuf::from(crate_dir).join(config.package_name()); 67 | 68 | if !integration_dir.exists() { 69 | continue; 70 | } 71 | 72 | if !integration_dir.join("Cargo.toml").exists() { 73 | continue; 74 | } 75 | 76 | quiet_println(&format!( 77 | "Running integration tests for crate: {}", 78 | crate_dir 79 | )); 80 | 81 | let _dir = sh.push_dir(&integration_dir); 82 | 83 | let available_versions = discover_version_features(sh, &integration_dir)?; 84 | if available_versions.is_empty() { 85 | quiet_println(" No version features found in Cargo.toml"); 86 | continue; 87 | } 88 | 89 | let versions_to_test: Vec = if let Some(config_versions) = &config.versions { 90 | // Filter available versions by config. 91 | let mut filtered = Vec::new(); 92 | for requested in config_versions { 93 | if available_versions.contains(requested) { 94 | filtered.push(requested.clone()); 95 | } else { 96 | return Err(format!( 97 | "Requested version '{}' not found in available versions: {}", 98 | requested, 99 | available_versions.join(", ") 100 | ) 101 | .into()); 102 | } 103 | } 104 | filtered 105 | } else { 106 | // No config, test all available versions. 107 | available_versions 108 | }; 109 | 110 | // Run tests for each version. 111 | for version in &versions_to_test { 112 | quiet_println(&format!(" Testing with version: {}", version)); 113 | quiet_cmd!(sh, "cargo --locked test --features={version}").run()?; 114 | } 115 | } 116 | 117 | Ok(()) 118 | } 119 | 120 | /// Discover all features from the integration package using cargo metadata. 121 | fn discover_version_features( 122 | sh: &Shell, 123 | integration_dir: &Path, 124 | ) -> Result, Box> { 125 | let _dir = sh.push_dir(integration_dir); 126 | let metadata = cmd!(sh, "cargo metadata --format-version 1 --no-deps").read()?; 127 | let json: serde_json::Value = serde_json::from_str(&metadata)?; 128 | 129 | let mut features = Vec::new(); 130 | 131 | // Find the package in the metadata and extract its features. 132 | if let Some(packages) = json["packages"].as_array() { 133 | // Should only be one package since we're in the integration test directory. 134 | if let Some(package) = packages.first() { 135 | if let Some(package_features) = package["features"].as_object() { 136 | for feature_name in package_features.keys() { 137 | features.push(feature_name.clone()); 138 | } 139 | } 140 | } 141 | } 142 | 143 | // Sort for consistent output. 144 | features.sort(); 145 | 146 | Ok(features) 147 | } 148 | -------------------------------------------------------------------------------- /releases/releases.json: -------------------------------------------------------------------------------- 1 | { 2 | "org": "gihub.com/rust-bitcoin", 3 | "latests": [ 4 | { "package": "bitcoincore-rpc", "version": "0.18.0" }, 5 | { "package": "bitcoincore-rpc-json", "version": "0.18.0"}, 6 | { "package": "miniscript", "version": "11.0.0" }, 7 | { "package": "bitcoin", "version": "0.32.0" }, 8 | { "package": "base58ck", "version": "0.1.0" }, 9 | { "package": "bech32", "version": "0.11.0" }, 10 | { "package": "bitcoin_hashes", "version": "0.14.0" }, 11 | { "package": "hex-conservative", "version": "0.2.0" }, 12 | { "package": "hex_lit", "version": "0.1.1" }, 13 | { "package": "bitcoin-internals", "version": "0.3.0" }, 14 | { "package": "bitcoin-io", "version": "0.1.2" }, 15 | { "package": "secp256k1", "version": "0.29.0" }, 16 | { "package": "units", "version": "0.1.0" }, 17 | { "package": "ordered", "version": "0.2.2" }, 18 | { "package": "bitcoinconsensus", "version": "0.106.0+26.0" } 19 | ], 20 | "releases": [ 21 | { "package": "bitcoincore-rpc", "version": "0.18.0", "dependencies": 22 | [ 23 | { "package": "bitcoincore-rpc-json", "version": "0.18.0"}, 24 | { "package": "log", "version": "0.4.5"}, 25 | { "package": "jsonrpc", "version": "0.14.0"}, 26 | { "package": "serde", "version": "1.0.156" }, 27 | { "package": "serde_json", "version": "1.0.96" } 28 | ] 29 | }, 30 | { "package": "bitcoincore-rpc", "version": "0.18.0", "dependencies": 31 | [ 32 | { "package": "serde", "version": "1.0.156" }, 33 | { "package": "serde_json", "version": "1.0.96" }, 34 | { "package": "bitcoin", "version": "0.31.0" } 35 | ] 36 | }, 37 | { "package": "miniscript", "version": "11.0.0", "dependencies": 38 | [ 39 | { "package": "bitcoin", "version": "0.31.0" }, 40 | { "package": "bitcoin-internals", "version": "0.2.0" }, 41 | { "package": "bech32", "version": "0.10.0-beta" }, 42 | { "package": "serde", "version": "1.0.103" } 43 | ] 44 | }, 45 | { "package": "bitcoin", "version": "0.32.0-rc1", "dependencies": 46 | [ 47 | { "package": "base58ck", "version": "0.1.0" }, 48 | { "package": "bech32", "version": "0.11.0" }, 49 | { "package": "bitcoin_hashes", "version": "0.14.0" }, 50 | { "package": "hex-conservative", "version": "0.2.0" }, 51 | { "package": "hex_lit", "version": "0.1.1" }, 52 | { "package": "bitcoin-internals", "version": "0.3.0" }, 53 | { "package": "bitcoin-io", "version": "0.1.1" }, 54 | { "package": "secp256k1", "version": "0.29.0" }, 55 | { "package": "units", "version": "0.1.0" }, 56 | { "package": "base64", "version": "0.21.3" }, 57 | { "package": "ordered", "version": "0.2.0" }, 58 | { "package": "bitcoinconsensus", "version": "0.105.0" }, 59 | { "package": "serde", "version": "1.0.103" } 60 | ] 61 | }, 62 | { "package": "bitcoin", "version": "0.31.2", "dependencies": 63 | [ 64 | { "package": "bitcoin-internals", "version": "0.2.0" }, 65 | { "package": "hex-conservative", "version": "0.1.1" }, 66 | { "package": "bech32", "version": "0.10.0-beta" }, 67 | { "package": "bitcoin_hashes", "version": "0.13.0" }, 68 | { "package": "secp256k1", "version": "0.28.0" }, 69 | { "package": "bitcoinconsensus", "version": "0.20.2-0.5.0" }, 70 | { "package": "hex_lit", "version": "0.1.1" }, 71 | { "package": "base64", "version": "0.21.3" }, 72 | { "package": "core2", "version": "0.3.2" }, 73 | { "package": "serde", "version": "1.0.103" } 74 | ] 75 | }, 76 | { "package": "base58ck", "version": "0.1.0", "dependencies": 77 | [ 78 | { "package": "bitcoin_hashes", "version": "0.14.0" }, 79 | { "package": "bitcoin-internals", "version": "0.3.0" } 80 | ] 81 | }, 82 | { "package": "bitcoin_hashes", "version": "0.13.0", "dependencies": 83 | [ 84 | { "package": "bitcoin-internals", "version": "0.2.0" }, 85 | { "package": "hex-conservative", "version": "0.1.1" }, 86 | { "package": "schemars", "version": "0.8.3" }, 87 | { "package": "serde", "version": "1.0.0" }, 88 | { "package": "core2", "version": "0.3.2" } 89 | ] 90 | }, 91 | { "package": "bitcoin-internals", "version": "0.2.0", "dependencies": 92 | [ 93 | { "package": "serde", "version": "1.0.103" } 94 | ] 95 | }, 96 | { "package": "bech32", "version": "0.10.0-beta", "dependencies": [] }, 97 | { "package": "secp256k1", "version": "0.28.0", "dependencies": 98 | [ 99 | { "package": "secp256k1-sys", "version": "0.9.0" }, 100 | { "package": "bitcoin_hashes", "version": "0.13.0" }, 101 | { "package": "rand", "version": "0.8.0" } 102 | ] 103 | }, 104 | { "package": "secp256k1-sys", "version": "0.9.0", "dependencies": [] }, 105 | { "package": "hex-conservative", "version": "0.1.1", "dependencies": 106 | [ 107 | { "package": "core2", "version": "0.3.2" } 108 | ] 109 | }, 110 | { "package": "bitcoinconsensus", "version": "0.20.2-0.5.0", "dependencies": 111 | [ 112 | { "package": "libc", "version": "0.2.0" } 113 | ] 114 | } 115 | ] 116 | } 117 | -------------------------------------------------------------------------------- /cargo-rbmt/README.md: -------------------------------------------------------------------------------- 1 | # Maintainer Tools 2 | 3 | Maintainer tools for Rust-based projects in the Bitcoin domain. Built with [xshell](https://github.com/matklad/xshell). 4 | 5 | ## Environment Variables 6 | 7 | * `RBMT_LOG_LEVEL=quiet` - Suppress verbose output and reduce cargo noise. 8 | 9 | ## Configuration 10 | 11 | Configuration for `rbmt` is stored in `rbmt.toml`. The file can live at both the workspace root (e.g. `$ROOT/rbmt.toml`) as well as per-package (e.g. `$ROOT/$PACKAGE/rbmt.toml`) within a repository. 12 | 13 | ### Lint 14 | 15 | The `lint` command detects duplicate dependencies, but some may be unavoidable (e.g., during dependency updates where transitive dependencies haven't caught up). Configure the `[lint]` section to whitelist specific duplicates for a workspace (or a package if only one package in a repository). 16 | 17 | ```toml 18 | [lint] 19 | allowed_duplicates = [ 20 | "syn", 21 | "bitcoin_hashes", 22 | ] 23 | ``` 24 | 25 | ### Test 26 | 27 | The `test` command can be configured to run feature matrix testing for your package. Configure with the `rbmt.toml` file at the package level. 28 | 29 | ```toml 30 | [test] 31 | # Examples to run with different feature configurations. 32 | # 33 | # Supported formats: 34 | # * "name" - runs with default features. 35 | # * "name:-" - runs with no-default-features. 36 | # * "name:feature1 feature2" - runs with specific features. 37 | examples = [ 38 | "bip32", # Default features 39 | "bip32:-", # No default features 40 | "bip32:serde rand", # Specific features 41 | ] 42 | 43 | # Features to test with the conventional `std` feature enabled. 44 | # Tests each feature alone with std, all pairs, and all together. 45 | # Example: ["serde", "rand"] tests: std+serde, std+rand, std+serde+rand 46 | features_with_std = ["serde", "rand"] 47 | 48 | # Features to test without the `std` feature. 49 | # Tests each feature alone, all pairs, and all together. 50 | # Example: ["serde", "rand"] tests: serde, rand, serde+rand 51 | features_without_std = ["serde", "rand"] 52 | 53 | # Exact feature combinations to test. 54 | # Use for packages that don't follow conventional `std` patterns. 55 | # Each inner array is tested as-is with no automatic combinations. 56 | # Example: [["serde", "rand"], ["rand"]] tests exactly those two combinations 57 | exact_features = [ 58 | ["serde", "rand"], 59 | ["rand"], 60 | ] 61 | 62 | # Features to test with an explicit `no-std` feature enabled. 63 | # Only use if your package has a `no-std` feature (rust-miniscript pattern). 64 | # Tests each feature with no-std, all pairs, and all together. 65 | # Example: ["serde", "rand"] tests: no-std+serde, no-std+rand, no-std+serde+rand 66 | features_with_no_std = ["serde", "rand"] 67 | ``` 68 | 69 | ### Integration 70 | 71 | The `integration` command is designed to work with the [`corepc`](https://github.com/rust-bitcoin/corepc) integration testing framework, which provides Bitcoin Core binaries and testing infrastructure. 72 | 73 | ```toml 74 | [integration] 75 | # Integration tests package name, defaults to "bitcoind-tests". 76 | package = "bitcoind-tests" 77 | # Versions to test. If omitted, tests all discovered versions from Cargo.toml. 78 | versions = ["29_0", "28_2", "27_2"] 79 | ``` 80 | 81 | ### Prerelease 82 | 83 | The `prerelease` command performs readiness checks before releasing a package. By default, all packages are checked unless they explicitly opt-out. 84 | 85 | ```toml 86 | [prerelease] 87 | # Set to true to skip pre-release checks for this package. 88 | # Use this for packages that are not yet ready for release. 89 | skip = true 90 | ``` 91 | 92 | ## Lock Files 93 | 94 | To ensure your package works with the full range of declared dependency versions, `cargo-rbmt` requires two lock files in your repository. 95 | 96 | * `Cargo-minimal.lock` - Minimum versions that satisfy your dependency constraints. 97 | * `Cargo-recent.lock` - Recent/updated versions of dependencies. 98 | 99 | The `lock` command generates and maintains these files for you. You can then use `--lock-file` with any command to test against either version set. 100 | 101 | ### Usage 102 | 103 | **Generate/update lock files** 104 | 105 | ```bash 106 | cargo rbmt lock 107 | ``` 108 | 109 | 1. Verify that direct dependency versions aren't being bumped by transitive dependencies. 110 | 2. Generate `Cargo-minimal.lock` with minimal versions across the entire dependency tree. 111 | 3. Update `Cargo-recent.lock` with conservatively updated dependencies. 112 | 113 | **Use a specific lock file** 114 | 115 | ```bash 116 | # Test with minimal versions. 117 | cargo rbmt --lock-file minimal test stable 118 | 119 | # Test with recent versions. 120 | cargo rbmt --lock-file recent test stable 121 | 122 | # Works with any command. 123 | cargo rbmt --lock-file minimal lint 124 | cargo rbmt --lock-file minimal docs 125 | ``` 126 | 127 | When you specify `--lock-file`, the tool copies that lock file to `Cargo.lock` before running the command. This allows you to test your code against different dependency version constraints. 128 | 129 | ## Workspace Integration 130 | 131 | `cargo-rbmt` can simply be installed globally on a system or added as a dev-dependency to a package. 132 | 133 | ### 1. Install on system 134 | 135 | Install the tool globally on your system with `cargo install`. 136 | 137 | ```bash 138 | cargo install cargo-rbmt@0.1.0 139 | ``` 140 | 141 | Then run from anywhere in your repository as a cargo subcommand. It can also be called directly as `cargo-rbmt`. 142 | 143 | ```bash 144 | cargo rbmt lint 145 | ``` 146 | 147 | ### 2. Add as a dev-dependency 148 | 149 | Add as a dev-dependency to a workspace member. This pins the tool version in your lockfile for reproducible builds. But this also means that `cargo-rbmt` dependencies could influence version resolution for the workspace. 150 | 151 | ```toml 152 | [dev-dependencies] 153 | cargo-rbmt = "0.1.0" 154 | ``` 155 | 156 | Then run via cargo. 157 | 158 | ```bash 159 | cargo run --bin cargo-rbmt -- lint 160 | ``` 161 | 162 | It might be worth wrapping in an [xtask](https://github.com/matklad/cargo-xtask) package for a clean interface. 163 | -------------------------------------------------------------------------------- /cargo-rbmt/src/prerelease.rs: -------------------------------------------------------------------------------- 1 | //! Pre-release readiness checks. 2 | 3 | use crate::environment::{get_crate_dirs, get_target_directory, quiet_println, CONFIG_FILE_PATH}; 4 | use crate::quiet_cmd; 5 | use serde::Deserialize; 6 | use std::fs; 7 | use std::io::{BufRead, BufReader}; 8 | use std::path::Path; 9 | use xshell::Shell; 10 | 11 | /// Pre-release configuration loaded from rbmt.toml. 12 | #[derive(Debug, Deserialize, Default)] 13 | #[serde(default)] 14 | struct Config { 15 | prerelease: PrereleaseConfig, 16 | } 17 | 18 | /// Pre-release-specific configuration. 19 | #[derive(Debug, Deserialize, Default)] 20 | #[serde(default)] 21 | struct PrereleaseConfig { 22 | /// If true, opt-out of pre-release checks for this package. 23 | skip: bool, 24 | } 25 | 26 | impl PrereleaseConfig { 27 | /// Load pre-release configuration from a package directory. 28 | fn load(package_dir: &Path) -> Result> { 29 | let config_path = package_dir.join(CONFIG_FILE_PATH); 30 | 31 | if !config_path.exists() { 32 | // Return default config (skip = false) if file doesn't exist. 33 | return Ok(PrereleaseConfig { skip: false }); 34 | } 35 | 36 | let contents = std::fs::read_to_string(&config_path)?; 37 | let config: Config = toml::from_str(&contents)?; 38 | Ok(config.prerelease) 39 | } 40 | } 41 | 42 | /// Run pre-release readiness checks for all packages. 43 | pub fn run(sh: &Shell, packages: &[String]) -> Result<(), Box> { 44 | let package_dirs = get_crate_dirs(sh, packages)?; 45 | quiet_println(&format!( 46 | "Running pre-release checks on {} packages", 47 | package_dirs.len() 48 | )); 49 | 50 | let mut skipped = Vec::new(); 51 | 52 | for package_dir in &package_dirs { 53 | let config = PrereleaseConfig::load(Path::new(package_dir))?; 54 | 55 | if config.skip { 56 | skipped.push(package_dir.as_str()); 57 | quiet_println(&format!( 58 | "Skipping package: {} (marked as skip)", 59 | package_dir 60 | )); 61 | continue; 62 | } 63 | 64 | quiet_println(&format!("Checking package: {}", package_dir)); 65 | 66 | let _dir = sh.push_dir(package_dir); 67 | 68 | // Run all pre-release checks. Return immediately on first failure. 69 | if let Err(e) = check_todos(sh) { 70 | eprintln!("Pre-release check failed for {}: {}", package_dir, e); 71 | return Err(e); 72 | } 73 | 74 | if let Err(e) = check_publish(sh) { 75 | eprintln!("Pre-release check failed for {}: {}", package_dir, e); 76 | return Err(e); 77 | } 78 | } 79 | 80 | quiet_println("All pre-release checks passed"); 81 | Ok(()) 82 | } 83 | 84 | /// Check for TODO and FIXME comments in source files. 85 | fn check_todos(sh: &Shell) -> Result<(), Box> { 86 | quiet_println("Checking for TODO and FIXME comments..."); 87 | 88 | let mut todos = Vec::new(); 89 | let src_dir = sh.current_dir().join("src"); 90 | 91 | // Recursively walk the src/ directory. 92 | let mut dirs_to_visit = vec![src_dir]; 93 | while let Some(dir) = dirs_to_visit.pop() { 94 | for entry in fs::read_dir(dir)? { 95 | let entry = entry?; 96 | let path = entry.path(); 97 | 98 | if path.is_dir() { 99 | dirs_to_visit.push(path); 100 | } else if path.extension().and_then(|s| s.to_str()) == Some("rs") { 101 | // Check Rust source files for TODO and FIXME comments. 102 | let file = fs::File::open(&path)?; 103 | let reader = BufReader::new(file); 104 | 105 | for (line_num, line) in reader.lines().enumerate() { 106 | let line = line?; 107 | if line.contains("// TODO") 108 | || line.contains("/* TODO") 109 | || line.contains("// FIXME") 110 | || line.contains("/* FIXME") 111 | { 112 | todos.push((path.clone(), line_num + 1, line)); 113 | } 114 | } 115 | } 116 | } 117 | } 118 | 119 | if !todos.is_empty() { 120 | eprintln!("\nFound {} TODO/FIXME comment(s):", todos.len()); 121 | for (file, line_num, line) in &todos { 122 | eprintln!("{}:{}:{}", file.display(), line_num, line.trim()); 123 | } 124 | return Err(format!("Found {} TODO/FIXME comments", todos.len()).into()); 125 | } 126 | 127 | quiet_println("No TODO or FIXME comments found"); 128 | Ok(()) 129 | } 130 | 131 | /// Check that the package can be published. 132 | /// 133 | /// A package may work with local path dependencies, but fail when published 134 | /// because the version specifications don't match the published versions 135 | /// or don't resolve correctly. 136 | fn check_publish(sh: &Shell) -> Result<(), Box> { 137 | quiet_cmd!(sh, "cargo publish --dry-run").run()?; 138 | let package_dir = get_publish_dir(sh)?; 139 | quiet_println(&format!("Testing publish {}...", package_dir)); 140 | 141 | let _dir = sh.push_dir(&package_dir); 142 | // Broad test to try and weed out any dependency issues. 143 | quiet_cmd!(sh, "cargo test --all-features --all-targets").run()?; 144 | quiet_println("Publish tests passed"); 145 | 146 | Ok(()) 147 | } 148 | 149 | /// Get the path to the publish directory for the current package from cargo metadata. 150 | fn get_publish_dir(sh: &Shell) -> Result> { 151 | let target_dir = get_target_directory(sh)?; 152 | 153 | // Find the package that matches the current directory. 154 | let metadata = quiet_cmd!(sh, "cargo metadata --no-deps --format-version 1").read()?; 155 | let json: serde_json::Value = serde_json::from_str(&metadata)?; 156 | let current_dir = sh.current_dir(); 157 | let current_manifest = current_dir.join("Cargo.toml"); 158 | 159 | let packages = json["packages"] 160 | .as_array() 161 | .ok_or("Missing 'packages' field in cargo metadata")?; 162 | 163 | for package in packages { 164 | let manifest_path = package["manifest_path"] 165 | .as_str() 166 | .ok_or("Missing manifest_path in package")?; 167 | 168 | if manifest_path == current_manifest.to_str().ok_or("Invalid path")? { 169 | let name = package["name"].as_str().ok_or("Missing name in package")?; 170 | 171 | let version = package["version"] 172 | .as_str() 173 | .ok_or("Missing version in package")?; 174 | 175 | return Ok(format!("{}/package/{}-{}", target_dir, name, version)); 176 | } 177 | } 178 | 179 | Err("Could not find current package in cargo metadata".into()) 180 | } 181 | -------------------------------------------------------------------------------- /ci/README.md: -------------------------------------------------------------------------------- 1 | # Continuous Integration 2 | 3 | This directory contains tools used by crates in the `rust-bitcoin` org to implement Continuous 4 | Integration. Currently this is just a script `run_task.sh` that can be called from a GitHub workflow 5 | job to run a specific task. 6 | 7 | TL;DR `./run_task.sh --help` 8 | 9 | #### Table Of Contents 10 | 11 | - [Usage](#usage) 12 | - [Lock file](#lock-file) 13 | - [Crates](#crates) 14 | * [Per crate environment variables](#per-crate-environment-variables) 15 | * [Additional crate specific tests](#additional-crate-specific-tests) 16 | - [Fuzzing](#fuzzing) 17 | - [Example workflows](#example-workflows) 18 | * [A job using a stable toolchain](#a-job-using-a-stable-toolchain) 19 | * [A job using a specific nightly toolchain](#a-job-using-a-specific-nightly-toolchain) 20 | 21 | ## Usage 22 | 23 | The `run_task.sh` script expects a few things to be present when it runs: 24 | 25 | In the repository root: 26 | 27 | - A lock file: `Cargo.lock` 28 | - A script that defines the crates: `contrib/crates.sh` 29 | 30 | And for each crate there should exist a directory `REPO_DIR/CRATE/contrib/` containing: 31 | 32 | - `test_vars.sh`: Defines environment variables 33 | - Optional: `extra_tests.sh`: Additional test script. 34 | - Optional: `extra_lints.sh`: Additional lint script. 35 | 36 | If the repository is not a workspace then per crate files go directly in `REPO_ROOT/contrib/`. 37 | 38 | (See [Crates](#crates) below.) 39 | 40 | ## Lock file 41 | 42 | Repositories MUST contain a `Cargo.lock` file before running `run_task.sh`. `cargo` is typically 43 | called with `--locked`. If you don't care about dependency versions just run `cargo update` in your 44 | CI job (to create a lock file) before calling `run_task.sh`. 45 | 46 | If you do care about versions consider adding: 47 | 48 | - `Cargo-recent.lock`: A manifest with some recent versions numbers that pass CI. 49 | - `Cargo-minimal.lock`: A manifest with some minimal version numbers that pass CI. 50 | 51 | Then you can use, for example: 52 | 53 | ```yaml 54 | strategy: 55 | matrix: 56 | dep: [minimal, recent] 57 | steps: 58 | 59 | 60 | 61 | - name: "Copy lock file" 62 | run: cp Cargo-${{ matrix.dep }}.lock Cargo.lock 63 | 64 | ``` 65 | 66 | (Tip: Create minimal lock file with`cargo +nightly build -- -Z minimal-versions`.) 67 | 68 | ## Crates 69 | 70 | All repositories MUST include a `REPO_DIR/contrib/crates.sh` script: 71 | 72 | ```bash 73 | #!/usr/bin/env bash 74 | 75 | # Crates in this workspace to test (note "fuzz" is only built not tested). 76 | CRATES=("base58" "bitcoin" "fuzz" "hashes" "internals" "io" "units") 77 | ``` 78 | 79 | `CRATES` MUST be an array. If repository is not a workspace use `CRATES=(".")`. 80 | 81 | ### Per crate environment variables 82 | 83 | All crates MUST include a file `REPO_DIR/CRATE/contrib/test_vars.sh` 84 | 85 | ```bash 86 | #!/usr/bin/env bash 87 | 88 | # Test all these features with "std" enabled. 89 | # 90 | # Ignore this if crate does not have "std" feature. 91 | FEATURES_WITH_STD="" 92 | 93 | # Test all these features without "std" enabled. 94 | # 95 | # Use this even if crate does not have "std" feature. 96 | FEATURES_WITHOUT_STD="" 97 | 98 | # Run these examples. 99 | EXAMPLES="" 100 | ``` 101 | 102 | #### The `EXAMPLES` variable 103 | 104 | ```bash 105 | EXAMPLES="example:feature" 106 | ``` 107 | 108 | ```bash 109 | EXAMPLES="example:feature1,feature2" 110 | ``` 111 | 112 | ```bash 113 | EXAMPLES="example_a:feature1,feature2 example_b:feature1" 114 | ``` 115 | 116 | 117 | Tip: if your example does not require any features consider using "default". 118 | 119 | ```bash 120 | EXAMPLES="example_a:default" 121 | ``` 122 | 123 | ### Additional crate specific tests 124 | 125 | Additional tests can be put in an optional `contrib/extra_tests.sh` script. This script will be run 126 | as part of the `stable`, `nightly`, and `msrv` jobs after running unit tests. 127 | 128 | ### Duplicate dependencies 129 | 130 | If any dependency should be ignored from the duplicate dependencies test (done when linting) specify 131 | them in a bash array in `REPO_DIR/contrib/whitelist_deps.sh` as such: 132 | 133 | Note, this is usually a temporary measure during upgrade. 134 | 135 | ```bash 136 | #!/usr/bin/env bash 137 | 138 | DUPLICATE_DEPS=("bech32") 139 | ``` 140 | 141 | ## Fuzzing 142 | 143 | Fuzz tests are expected to be in a crate called `REPO_DIR/fuzz/`. The `run_task.sh` script just 144 | builds the fuzz crate as a sanity check. 145 | 146 | ## Example workflows 147 | 148 | ### A job using a stable toolchain 149 | 150 | To use the `run_task.sh` script you'll want to do something like this: 151 | 152 | ```yaml 153 | jobs: 154 | Stable: # 2 jobs, one per manifest. 155 | name: Test - stable toolchain 156 | runs-on: ubuntu-latest 157 | strategy: 158 | fail-fast: false 159 | matrix: 160 | dep: [minimal, recent] 161 | steps: 162 | - name: "Checkout repo" 163 | uses: actions/checkout@v4 164 | - name: "Checkout maintainer tools" 165 | uses: actions/checkout@v4 166 | with: 167 | repository: rust-bitcoin/rust-bitcoin-maintainer-tools 168 | path: maintainer-tools 169 | - name: "Select toolchain" 170 | uses: dtolnay/rust-toolchain@stable 171 | - name: "Copy lock file" 172 | run: cp Cargo-${{ matrix.dep }}.lock Cargo.lock 173 | - name: "Run test script" 174 | run: ./maintainer-tools/ci/run_task.sh stable 175 | ``` 176 | 177 | ### A job using a specific nightly toolchain 178 | 179 | Have a file in the repository root with the nightly toolchain version to use. 180 | 181 | ```bash 182 | $ cat nightly-version 183 | nightly-2024-04-30 184 | ``` 185 | 186 | And use a `Prepare` job to a set an environment variable using the file. 187 | 188 | ```yaml 189 | jobs: 190 | Prepare: 191 | runs-on: ubuntu-latest 192 | outputs: 193 | nightly_version: ${{ steps.read_toolchain.outputs.nightly_version }} 194 | steps: 195 | - name: Checkout Crate 196 | uses: actions/checkout@v4 197 | - name: Read nightly version 198 | id: read_toolchain 199 | run: echo "nightly_version=$(cat nightly-version)" >> $GITHUB_OUTPUT 200 | 201 | Nightly: # 2 jobs, one per manifest. 202 | name: Test - nightly toolchain 203 | needs: Prepare 204 | runs-on: ubuntu-latest 205 | strategy: 206 | fail-fast: false 207 | matrix: 208 | dep: [minimal, recent] 209 | steps: 210 | - name: "Checkout repo" 211 | uses: actions/checkout@v4 212 | - name: "Checkout maintainer tools" 213 | uses: actions/checkout@v4 214 | with: 215 | repository: rust-bitcoin/rust-bitcoin-maintainer-tools 216 | path: maintainer-tools 217 | - name: "Select toolchain" 218 | uses: dtolnay/rust-toolchain@v1 219 | with: 220 | toolchain: ${{ needs.Prepare.outputs.nightly_version }} 221 | - name: "Copy lock file" 222 | run: cp Cargo-${{ matrix.dep }}.lock Cargo.lock 223 | - name: "Run test script" 224 | run: ./maintainer-tools/ci/run_task.sh nightly 225 | ``` 226 | -------------------------------------------------------------------------------- /cargo-rbmt/src/lint.rs: -------------------------------------------------------------------------------- 1 | use std::fs; 2 | use std::path::Path; 3 | use xshell::Shell; 4 | 5 | use crate::environment::{get_crate_dirs, quiet_println, CONFIG_FILE_PATH}; 6 | use crate::quiet_cmd; 7 | use crate::toolchain::{check_toolchain, Toolchain}; 8 | 9 | /// Lint configuration loaded from rbmt.toml. 10 | #[derive(Debug, serde::Deserialize, Default)] 11 | #[serde(default)] 12 | struct Config { 13 | lint: LintConfig, 14 | } 15 | 16 | /// Lint-specific configuration. 17 | #[derive(Debug, serde::Deserialize, Default)] 18 | #[serde(default)] 19 | struct LintConfig { 20 | /// List of crate names that are allowed to have duplicate versions. 21 | allowed_duplicates: Vec, 22 | } 23 | 24 | impl LintConfig { 25 | /// Load lint configuration from the workspace root. 26 | fn load(sh: &Shell) -> Result> { 27 | let config_path = sh.current_dir().join(CONFIG_FILE_PATH); 28 | 29 | if !config_path.exists() { 30 | // Return empty config if file doesn't exist. 31 | return Ok(LintConfig { 32 | allowed_duplicates: Vec::new(), 33 | }); 34 | } 35 | 36 | let contents = fs::read_to_string(&config_path)?; 37 | let config: Config = toml::from_str(&contents)?; 38 | Ok(config.lint) 39 | } 40 | } 41 | 42 | /// Run the lint task. 43 | pub fn run(sh: &Shell, packages: &[String]) -> Result<(), Box> { 44 | check_toolchain(sh, Toolchain::Nightly)?; 45 | quiet_println("Running lint task..."); 46 | 47 | lint_workspace(sh)?; 48 | lint_crates(sh, packages)?; 49 | check_duplicate_deps(sh)?; 50 | check_clippy_toml_msrv(sh, packages)?; 51 | 52 | quiet_println("Lint task completed successfully"); 53 | Ok(()) 54 | } 55 | 56 | /// Lint the workspace with clippy. 57 | fn lint_workspace(sh: &Shell) -> Result<(), Box> { 58 | quiet_println("Linting workspace..."); 59 | 60 | // Run clippy on workspace with all features. 61 | quiet_cmd!(sh, "cargo --locked clippy --workspace --all-targets --all-features --keep-going") 62 | .args(&["--", "-D", "warnings"]) 63 | .run()?; 64 | 65 | // Run clippy on workspace without features. 66 | quiet_cmd!(sh, "cargo --locked clippy --workspace --all-targets --keep-going") 67 | .args(&["--", "-D", "warnings"]) 68 | .run()?; 69 | 70 | Ok(()) 71 | } 72 | 73 | /// Run extra crate-specific lints. 74 | /// 75 | /// # Why run at the crate level? 76 | /// 77 | /// When running `cargo clippy --workspace --no-default-features`, cargo resolves 78 | /// features across the entire workspace, which can enable features through dependencies 79 | /// even when a crate's own default features are disabled. Running clippy on each crate 80 | /// individually ensures that each crate truly compiles and passes lints with only its 81 | /// explicitly enabled features. 82 | fn lint_crates(sh: &Shell, packages: &[String]) -> Result<(), Box> { 83 | quiet_println("Running crate-specific lints..."); 84 | 85 | let crate_dirs = get_crate_dirs(sh, packages)?; 86 | quiet_println(&format!("Found crates: {}", crate_dirs.join(", "))); 87 | 88 | for crate_dir in crate_dirs { 89 | // Returns a RAII guard which reverts the working directory to the old value when dropped. 90 | let _old_dir = sh.push_dir(&crate_dir); 91 | 92 | // Run clippy without default features. 93 | quiet_cmd!(sh, "cargo --locked clippy --all-targets --no-default-features --keep-going") 94 | .args(&["--", "-D", "warnings"]) 95 | .run()?; 96 | } 97 | 98 | Ok(()) 99 | } 100 | 101 | /// Check for duplicate dependencies. 102 | fn check_duplicate_deps(sh: &Shell) -> Result<(), Box> { 103 | quiet_println("Checking for duplicate dependencies..."); 104 | 105 | let config = LintConfig::load(sh)?; 106 | let allowed_duplicates = &config.allowed_duplicates; 107 | 108 | // Run cargo tree to find duplicates. 109 | let output = quiet_cmd!(sh, "cargo --locked tree --target=all --all-features --duplicates") 110 | .ignore_status() 111 | .read()?; 112 | 113 | let duplicates: Vec<&str> = output 114 | .lines() 115 | // Filter out non crate names. 116 | .filter(|line| line.chars().next().is_some_and(|c| c.is_alphanumeric())) 117 | // Filter out whitelisted crates. 118 | .filter(|line| { 119 | !allowed_duplicates 120 | .iter() 121 | .any(|allowed| line.contains(allowed)) 122 | }) 123 | .collect(); 124 | 125 | if !duplicates.is_empty() { 126 | // Show full tree for context. 127 | quiet_cmd!(sh, "cargo --locked tree --target=all --all-features --duplicates").run()?; 128 | eprintln!("Error: Found duplicate dependencies in workspace!"); 129 | for dup in &duplicates { 130 | eprintln!(" {}", dup); 131 | } 132 | return Err("Dependency tree contains duplicates".into()); 133 | } 134 | 135 | quiet_println("No duplicate dependencies found"); 136 | Ok(()) 137 | } 138 | 139 | /// Check for deprecated clippy.toml MSRV settings. 140 | /// 141 | /// The bitcoin ecosystem has moved to Rust 1.74+ and should use Cargo.toml 142 | /// package.rust-version instead of clippy.toml msrv settings. 143 | fn check_clippy_toml_msrv( 144 | sh: &Shell, 145 | packages: &[String], 146 | ) -> Result<(), Box> { 147 | const CLIPPY_CONFIG_FILES: &[&str] = &["clippy.toml", ".clippy.toml"]; 148 | 149 | quiet_println("Checking for deprecated clippy.toml MSRV settings..."); 150 | 151 | let mut clippy_files = Vec::new(); 152 | 153 | // Check workspace root. 154 | let workspace_root = sh.current_dir(); 155 | for filename in CLIPPY_CONFIG_FILES { 156 | let path = workspace_root.join(filename); 157 | if path.exists() { 158 | clippy_files.push(path); 159 | } 160 | } 161 | 162 | // Check each package. 163 | let crate_dirs = get_crate_dirs(sh, packages)?; 164 | for crate_dir in crate_dirs { 165 | for filename in CLIPPY_CONFIG_FILES { 166 | let path = Path::new(&crate_dir).join(filename); 167 | if path.exists() { 168 | clippy_files.push(path); 169 | } 170 | } 171 | } 172 | 173 | // Check each clippy file for the msrv setting. 174 | let mut problematic_files = Vec::new(); 175 | for path in clippy_files { 176 | let contents = fs::read_to_string(&path)?; 177 | let config: toml::Value = toml::from_str(&contents)?; 178 | 179 | if config.get("msrv").is_some() { 180 | problematic_files.push(path.display().to_string()); 181 | } 182 | } 183 | 184 | if !problematic_files.is_empty() { 185 | eprintln!( 186 | "\nError: Found MSRV in clippy.toml, use Cargo.toml package.rust-version instead:" 187 | ); 188 | for file in &problematic_files { 189 | eprintln!(" {}", file); 190 | } 191 | return Err("MSRV should be specified in Cargo.toml, not clippy.toml".into()); 192 | } 193 | 194 | quiet_println("No deprecated clippy.toml MSRV settings found"); 195 | Ok(()) 196 | } 197 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | 123 | -------------------------------------------------------------------------------- /releases/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::path::{Path, PathBuf}; 2 | use std::time::Duration; 3 | use std::{fs, process}; 4 | 5 | use anyhow::Context; 6 | use clap::{arg, command, value_parser, Command}; 7 | use crates_io_api::AsyncClient; 8 | use releases::{json, Config, CrateVersion}; 9 | use semver::Version; 10 | use toml::Table; 11 | 12 | /// A green tick in UTF-8. 13 | const TICK: &str = "\x1b[92m\u{2713}\x1b[0m"; 14 | 15 | /// A red cross in UTF-8. 16 | const CROSS: &str = "\x1b[91m\u{2717}\x1b[0m"; 17 | 18 | /// API rate limit. 19 | const RATE_LIMIT_MILLIS: u64 = 100; 20 | 21 | /// The json file with all the versions in it. 22 | const DEFAULT_CONFIG_FILE: &str = "./releases.json"; 23 | 24 | // (Tobin) I'm not sure what this email address is used for, I guess its as a point of contact for 25 | // API abuse - if that is the case then using an address that will get back to me is ok. 26 | const DEFAULT_USER_AGENT: &str = "releases_bot (releases_bot@tobin.cc)"; 27 | 28 | #[tokio::main] 29 | async fn main() -> anyhow::Result<()> { 30 | // FIXME: I can't get clap to set these with `.default_value`? 31 | let mut config_file = PathBuf::from(DEFAULT_CONFIG_FILE); 32 | let mut user_agent = DEFAULT_USER_AGENT; 33 | 34 | let matches = command!() // requires `cargo` feature 35 | .arg( 36 | arg!( 37 | --user_agent "Set the user agent" 38 | ) 39 | .required(false) 40 | .value_parser(value_parser!(String)), 41 | ) 42 | .arg( 43 | arg!( 44 | -c --config "Sets a custom config file" 45 | ) 46 | .required(false) 47 | .value_parser(value_parser!(PathBuf)), 48 | ) 49 | .arg( 50 | arg!( 51 | -d --debug ... "Turn debugging information on" 52 | ) 53 | .required(false) 54 | .value_parser(value_parser!(u8)), 55 | ) 56 | .subcommand( 57 | Command::new("show-latest-releases") 58 | .about("show latest expected releases from config file"), 59 | ) 60 | .subcommand( 61 | Command::new("check-latest-releases").about( 62 | "check latest releases from config file against latest releases on crates.io", 63 | ), 64 | ) 65 | .subcommand( 66 | Command::new("check-latest-dependencies") 67 | .about( 68 | "check repo uses the latest releases (from crates.io) for all its dependencies", 69 | ) 70 | .arg( 71 | arg!([repository] "Path to the repository to check") 72 | .required(true) 73 | .value_parser(value_parser!(PathBuf)), 74 | ) 75 | .arg( 76 | arg!([crate_name] "Crate name (only required for workspace)") 77 | .required(false) 78 | .value_parser(value_parser!(String)), 79 | ), 80 | ) 81 | .get_matches(); 82 | 83 | // Flags can have multiple occurrences, but we don't currently support verbose debugging output. 84 | let debug = *matches.get_one::("debug").expect("Count's are defaulted"); 85 | if debug > 0 { 86 | println!("Debugging is on"); 87 | } 88 | 89 | if let Some(file) = matches.get_one::("config") { 90 | config_file = file.to_path_buf(); 91 | } 92 | if debug > 0 { 93 | println!("Using config file: {}", config_file.display()); 94 | } 95 | 96 | if let Some(agent) = matches.get_one::("user_agent") { 97 | user_agent = agent; 98 | } 99 | 100 | let config = read_config_file(&config_file)?; 101 | 102 | if matches.subcommand_matches("show-latest-releases").is_some() { 103 | show_releases(&config.latests)?; 104 | process::exit(0); 105 | } 106 | 107 | // Everything else needs the API client. 108 | let cli = AsyncClient::new(user_agent, Duration::from_millis(RATE_LIMIT_MILLIS))?; 109 | 110 | if matches.subcommand_matches("check-latest-releases").is_some() { 111 | check_latest_releases(&cli, &config.latests, debug).await?; 112 | process::exit(0); 113 | } 114 | 115 | if let Some(sub) = matches.subcommand_matches("check-latest-dependencies") { 116 | let repo = sub.get_one::("repository").expect("missing directory argument"); 117 | let crate_name = sub.get_one::("crate_name"); 118 | check_latest_dependencies(&cli, repo, crate_name, debug).await?; 119 | } 120 | 121 | Ok(()) 122 | } 123 | 124 | fn read_config_file(file: &Path) -> anyhow::Result { 125 | let data = fs::read_to_string(file)?; 126 | let json: json::Config = serde_json::from_str(&data)?; 127 | let config = Config::try_from(json)?; 128 | 129 | Ok(config) 130 | } 131 | 132 | /// Prints a list of `releases`. 133 | fn show_releases(releases: &[CrateVersion]) -> anyhow::Result<()> { 134 | println!(); 135 | for release in releases { 136 | println!(" - {:20} {}", release.package, release.version); 137 | } 138 | 139 | Ok(()) 140 | } 141 | 142 | /// Checks the releases in `latests` against the latest releases on crates.io 143 | async fn check_latest_releases( 144 | cli: &AsyncClient, 145 | latests: &[CrateVersion], 146 | _debug: u8, 147 | ) -> anyhow::Result<()> { 148 | let mut found_stale = false; 149 | 150 | println!( 151 | "\nChecking release versions in config file against the latest release on crates.io\n" 152 | ); 153 | 154 | for latest in latests { 155 | let released = api_latest(cli, &latest.package).await?; 156 | if latest.version != released { 157 | found_stale = true; 158 | println!( 159 | "Latest crates.io release {} {} does not match the config file latest version {}", 160 | latest.package, released, latest.version 161 | ); 162 | } else { 163 | println!("{} {} \x1b[92m\u{2713}\x1b[0m", latest.package, latest.version); 164 | } 165 | } 166 | 167 | if !found_stale { 168 | println!("\nAll releases on crates.io match those in the config file") 169 | } 170 | Ok(()) 171 | } 172 | 173 | /// Checks if a crate in `crate_dir` is using the latest dependencies released on `crates.io`. 174 | async fn check_latest_dependencies( 175 | cli: &AsyncClient, 176 | repo_dir: &Path, 177 | crate_name: Option<&String>, 178 | _debug: u8, 179 | ) -> anyhow::Result<()> { 180 | let mut path = repo_dir.to_path_buf(); 181 | if let Some(name) = crate_name { 182 | path.push(name); 183 | }; 184 | path.push("Cargo.toml"); 185 | 186 | let crate_name = match crate_name { 187 | Some(name) => name, 188 | // next_back is equivalent to last() but more efficient 189 | None => repo_dir.iter().next_back().expect("invalid repository").to_str().unwrap(), 190 | }; 191 | 192 | let data = fs::read_to_string(&path) 193 | .with_context(|| format!("Failed to read manifest from {}", path.display()))?; 194 | 195 | let manifest = data.parse::()?; 196 | 197 | let package_section = manifest["package"].as_table().expect("manifest has package section"); 198 | let crate_version = package_section.get("version").expect("all crates have a version"); 199 | 200 | println!("\nChecking latest dependencies for:"); 201 | println!(" crate: {}", crate_name); 202 | println!(" version: {}", crate_version); 203 | println!(" manifest: {}", path.display()); 204 | println!(); 205 | 206 | let dependencies_section = 207 | manifest["dependencies"].as_table().expect("manifest has dependencies section"); 208 | let mut deps: Vec = dependencies_section.keys().map(Clone::clone).collect(); 209 | deps.sort(); 210 | for key in deps { 211 | let value = dependencies_section.get(&key).expect("we know this key exists"); 212 | let package = match value.as_table() { 213 | Some(t) => match t.get("package") { 214 | Some(v) => v.as_str().unwrap_or(&key), 215 | None => &key, 216 | }, 217 | None => &key, 218 | }; 219 | let version = match value.as_table() { 220 | Some(t) => match t.get("version") { 221 | Some(v) => v.as_str(), 222 | None => None, 223 | }, 224 | None => value.as_str(), 225 | }; 226 | 227 | let latest = api_latest(cli, package).await?; 228 | 229 | // If version is not specified in the manifest cargo uses latest. 230 | match version { 231 | Some(version) => { 232 | let version = Version::parse(version)?; 233 | if latest.major != version.major || latest.minor != version.minor { 234 | println!(" - {:20} {} {} latest: {}", package, CROSS, version, latest); 235 | } else if latest.patch != version.patch { 236 | println!(" - {:20} {} {} latest: {}", package, TICK, version, latest); 237 | } else { 238 | println!(" - {:20} {} {}", package, TICK, latest); 239 | } 240 | } 241 | None => println!(" - {:20} {}", package, latest), 242 | }; 243 | } 244 | 245 | Ok(()) 246 | } 247 | 248 | /// Gets the latest released version of `package` from `crates.io`. 249 | async fn api_latest(cli: &AsyncClient, package: &str) -> anyhow::Result { 250 | let response = cli.get_crate(package).await?; 251 | Ok(Version::parse(&response.crate_data.max_version)?) 252 | } 253 | -------------------------------------------------------------------------------- /cargo-rbmt/src/test.rs: -------------------------------------------------------------------------------- 1 | //! Test tasks with feature matrix testing. 2 | 3 | use crate::environment::{get_crate_dirs, quiet_println, CONFIG_FILE_PATH}; 4 | use crate::quiet_cmd; 5 | use crate::toolchain::{check_toolchain, Toolchain}; 6 | use serde::Deserialize; 7 | use std::ffi::OsStr; 8 | use std::fmt; 9 | use std::path::Path; 10 | use xshell::Shell; 11 | 12 | /// Conventinal feature flags used across rust-bitcoin crates. 13 | #[derive(Debug, Clone, Copy)] 14 | enum FeatureFlag { 15 | /// Enable the standard library. 16 | Std, 17 | /// Legacy feature to disable standard library. 18 | NoStd, 19 | } 20 | 21 | impl FeatureFlag { 22 | /// Get the feature string for this flag. 23 | fn as_str(&self) -> &'static str { 24 | match self { 25 | FeatureFlag::Std => "std", 26 | FeatureFlag::NoStd => "no-std", 27 | } 28 | } 29 | } 30 | 31 | impl fmt::Display for FeatureFlag { 32 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 33 | write!(f, "{}", self.as_str()) 34 | } 35 | } 36 | 37 | impl AsRef for FeatureFlag { 38 | fn as_ref(&self) -> &str { 39 | self.as_str() 40 | } 41 | } 42 | 43 | impl AsRef for FeatureFlag { 44 | fn as_ref(&self) -> &OsStr { 45 | OsStr::new(self.as_str()) 46 | } 47 | } 48 | 49 | /// Test configuration loaded from rbmt.toml. 50 | #[derive(Debug, Deserialize, Default)] 51 | #[serde(default)] 52 | struct Config { 53 | test: TestConfig, 54 | } 55 | 56 | /// Test-specific configuration. 57 | #[derive(Debug, Deserialize, Default)] 58 | #[serde(default)] 59 | struct TestConfig { 60 | /// Examples to run with different feature configurations. 61 | /// 62 | /// Supported formats: 63 | /// * `"name"` - runs with default features. 64 | /// * `"name:-"` - runs with no-default-features. 65 | /// * `"name:feature1 feature2"` - runs with specific features. 66 | /// 67 | /// # Examples 68 | /// 69 | /// ``` 70 | /// examples = [ 71 | /// "bip32", 72 | /// "bip32:-", 73 | /// "bip32:serde rand" 74 | /// ] 75 | /// ``` 76 | examples: Vec, 77 | 78 | /// List of individual features to test with the conventional `std` feature enabled. 79 | /// Automatically tests feature combinations, alone with `std`, all pairs, and all together. 80 | /// 81 | /// # Examples 82 | /// 83 | /// `["serde", "rand"]` tests `std+serde`, `std+rand`, `std+serde+rand`. 84 | features_with_std: Vec, 85 | 86 | /// List of individual features to test without the `std` feature. 87 | /// Automatically tests features combinations, each feature alone, 88 | /// all pairs, and all together. 89 | /// 90 | /// # Examples 91 | /// 92 | /// `["serde", "rand"]` tests `serde`, `rand`, `serde+rand`. 93 | features_without_std: Vec, 94 | 95 | /// Exact feature combinations to test. 96 | /// Use for crates that don't follow the conventional `std` feature pattern. 97 | /// Each inner vector is a list of features to test together. There is 98 | /// no automatic combinations of features tests. 99 | /// 100 | /// # Examples 101 | /// 102 | /// `[["serde", "rand"], ["rand"]]` tests exactly those two combinations. 103 | exact_features: Vec>, 104 | 105 | /// List of individual features to test with the `no-std` feature enabled. 106 | /// Only use if your crate has an explicit `no-std` feature (rust-miniscript pattern). 107 | /// Automatically tests each feature alone with `no-std`, all pairs, and all together. 108 | /// 109 | /// # Examples 110 | /// 111 | /// `["serde", "rand"]` tests `no-std+serde`, `no-std+serde`, `no-std+serde+rand`. 112 | features_with_no_std: Vec, 113 | } 114 | 115 | impl TestConfig { 116 | /// Load test configuration from a crate directory. 117 | fn load(crate_dir: &Path) -> Result> { 118 | let config_path = crate_dir.join(CONFIG_FILE_PATH); 119 | 120 | if !config_path.exists() { 121 | // Return empty config if file doesn't exist. 122 | return Ok(TestConfig { 123 | examples: Vec::new(), 124 | features_with_std: Vec::new(), 125 | features_without_std: Vec::new(), 126 | exact_features: Vec::new(), 127 | features_with_no_std: Vec::new(), 128 | }); 129 | } 130 | 131 | let contents = std::fs::read_to_string(&config_path)?; 132 | let config: Config = toml::from_str(&contents)?; 133 | Ok(config.test) 134 | } 135 | } 136 | 137 | /// Run tests for all crates with the specified toolchain. 138 | pub fn run( 139 | sh: &Shell, 140 | toolchain: Toolchain, 141 | no_debug_assertions: bool, 142 | packages: &[String], 143 | ) -> Result<(), Box> { 144 | let crate_dirs = get_crate_dirs(sh, packages)?; 145 | quiet_println(&format!("Testing {} crates", crate_dirs.len())); 146 | 147 | // Configure RUSTFLAGS for debug assertions. 148 | let _env = sh.push_env( 149 | "RUSTFLAGS", 150 | if no_debug_assertions { 151 | "-C debug-assertions=off" 152 | } else { 153 | "-C debug-assertions=on" 154 | }, 155 | ); 156 | 157 | for crate_dir in &crate_dirs { 158 | quiet_println(&format!("Testing crate: {}", crate_dir)); 159 | 160 | let _dir = sh.push_dir(crate_dir); 161 | // Check the package's MSRV, not the workspace root. 162 | check_toolchain(sh, toolchain)?; 163 | let config = TestConfig::load(Path::new(crate_dir))?; 164 | 165 | do_test(sh, &config)?; 166 | do_feature_matrix(sh, &config)?; 167 | } 168 | 169 | Ok(()) 170 | } 171 | 172 | /// Run basic test and examples. 173 | fn do_test(sh: &Shell, config: &TestConfig) -> Result<(), Box> { 174 | quiet_println("Running basic tests"); 175 | 176 | // Basic test (includes build). 177 | quiet_cmd!(sh, "cargo --locked test").run()?; 178 | 179 | // Run examples. 180 | for example in &config.examples { 181 | let parts: Vec<&str> = example.split(':').collect(); 182 | 183 | match parts.len() { 184 | 1 => { 185 | // Format: "name" - run with default features. 186 | let name = parts[0]; 187 | quiet_cmd!(sh, "cargo --locked run --example {name}").run()?; 188 | } 189 | 2 => { 190 | let name = parts[0]; 191 | let features = parts[1]; 192 | 193 | if features == "-" { 194 | // Format: "name:-" - run with no-default-features. 195 | quiet_cmd!(sh, "cargo --locked run --no-default-features --example {name}").run()?; 196 | } else { 197 | // Format: "name:features" - run with specific features. 198 | quiet_cmd!(sh, "cargo --locked run --example {name} --features={features}").run()?; 199 | } 200 | } 201 | _ => { 202 | return Err(format!( 203 | "Invalid example format: {}, expected 'name', 'name:-', or 'name:features'", 204 | example 205 | ) 206 | .into()); 207 | } 208 | } 209 | } 210 | 211 | Ok(()) 212 | } 213 | 214 | /// Run feature matrix tests. 215 | fn do_feature_matrix(sh: &Shell, config: &TestConfig) -> Result<(), Box> { 216 | quiet_println("Running feature matrix tests"); 217 | 218 | // Handle exact features (for unusual crates). 219 | if !config.exact_features.is_empty() { 220 | for features in &config.exact_features { 221 | let features_str = features.join(" "); 222 | quiet_println(&format!("Testing exact features: {}", features_str)); 223 | quiet_cmd!(sh, "cargo --locked test --no-default-features --features={features_str}").run()?; 224 | } 225 | return Ok(()); 226 | } 227 | 228 | // Handle no-std pattern (rust-miniscript). 229 | if !config.features_with_no_std.is_empty() { 230 | let no_std = FeatureFlag::NoStd; 231 | quiet_println("Testing no-std"); 232 | quiet_cmd!(sh, "cargo --locked test --no-default-features --features={no_std}").run()?; 233 | 234 | loop_features(sh, Some(FeatureFlag::NoStd), &config.features_with_no_std)?; 235 | } else { 236 | quiet_println("Testing no-default-features"); 237 | quiet_cmd!(sh, "cargo --locked test --no-default-features").run()?; 238 | } 239 | 240 | // Test all features. 241 | quiet_println("Testing all-features"); 242 | quiet_cmd!(sh, "cargo --locked test --all-features").run()?; 243 | 244 | // Test features with std. 245 | if !config.features_with_std.is_empty() { 246 | loop_features(sh, Some(FeatureFlag::Std), &config.features_with_std)?; 247 | } 248 | 249 | // Test features without std. 250 | if !config.features_without_std.is_empty() { 251 | loop_features(sh, None, &config.features_without_std)?; 252 | } 253 | 254 | Ok(()) 255 | } 256 | 257 | /// Test each feature individually and all combinations of two features. 258 | /// 259 | /// This implements three feature matrix testing strategies: 260 | /// 1. All features together (base feature + all test features). 261 | /// 2. Each feature individually (base feature + one test feature). 262 | /// 3. All unique pairs of test features (base feature + two test features). 263 | /// 264 | /// The pair testing catches feature interaction bugs (where two features work 265 | /// independently, but conflict when combined) while keeping test time manageable. 266 | /// 267 | /// # Parameters 268 | /// 269 | /// * `base` - Optional base feature that is always included (e.g., `Some(FeatureFlag::Std)`). 270 | /// * `features` - Features to test in combination. 271 | fn loop_features>( 272 | sh: &Shell, 273 | base: Option, 274 | features: &[S], 275 | ) -> Result<(), Box> { 276 | // Helper to combine base flag and features into a feature flag string. 277 | fn combine_features>(base: Option, additional: &[S]) -> String { 278 | match base { 279 | Some(flag) => std::iter::once(flag.as_ref()) 280 | .chain(additional.iter().map(|s| s.as_ref())) 281 | .collect::>() 282 | .join(" "), 283 | None => additional 284 | .iter() 285 | .map(|s| s.as_ref()) 286 | .collect::>() 287 | .join(" "), 288 | } 289 | } 290 | 291 | // Test all features together. 292 | let all_features = combine_features(base, features); 293 | quiet_println(&format!("Testing features: {}", all_features)); 294 | quiet_cmd!(sh, "cargo --locked test --no-default-features --features={all_features}").run()?; 295 | 296 | // Test each feature individually and all pairs (only if more than one feature). 297 | if features.len() > 1 { 298 | for i in 0..features.len() { 299 | let feature_combo = combine_features(base, &features[i..=i]); 300 | quiet_println(&format!("Testing features: {}", feature_combo)); 301 | quiet_cmd!(sh, "cargo --locked test --no-default-features --features={feature_combo}").run()?; 302 | 303 | // Test all pairs with features[i]. 304 | for j in (i + 1)..features.len() { 305 | let pair = [&features[i], &features[j]]; 306 | let feature_combo = combine_features(base, &pair); 307 | quiet_println(&format!("Testing features: {}", feature_combo)); 308 | quiet_cmd!(sh, "cargo --locked test --no-default-features --features={feature_combo}").run()?; 309 | } 310 | } 311 | } 312 | 313 | Ok(()) 314 | } 315 | -------------------------------------------------------------------------------- /ci/run_task.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Script used to run CI jobs, can also be used from the command line. 4 | # 5 | # Shellcheck can't search dynamic paths 6 | # shellcheck source=/dev/null 7 | 8 | set -euo pipefail 9 | 10 | REPO_DIR=$(git rev-parse --show-toplevel) 11 | 12 | # Make cargo invocations verbose unless in quiet mode. 13 | # Also control bash debug output based on log level. 14 | case "${MAINTAINER_TOOLS_LOG_LEVEL:-verbose}" in 15 | quiet) 16 | export CARGO_TERM_VERBOSE=false 17 | export CARGO_TERM_QUIET=true 18 | ;; 19 | *) 20 | export CARGO_TERM_VERBOSE=true 21 | export CARGO_TERM_QUIET=false 22 | set -x 23 | ;; 24 | esac 25 | 26 | # Use the current `Cargo.lock` file without updating it. 27 | cargo="cargo --locked" 28 | 29 | usage() { 30 | cat < /dev/null 154 | 155 | do_test 156 | do_feature_matrix 157 | 158 | popd > /dev/null 159 | done 160 | } 161 | 162 | do_test() { 163 | # Defaults / sanity checks 164 | $cargo build 165 | $cargo test 166 | 167 | if [ -n "${EXAMPLES}" ]; then 168 | for example in $EXAMPLES; do # EXAMPLES is set in contrib/test_vars.sh 169 | name="$(echo "$example" | cut -d ':' -f 1)" 170 | features="$(echo "$example" | cut -d ':' -f 2)" 171 | $cargo run --example "$name" --features="$features" 172 | done 173 | fi 174 | 175 | if [ -e ./contrib/extra_tests.sh ]; 176 | then 177 | . ./contrib/extra_tests.sh 178 | fi 179 | } 180 | 181 | # Each crate defines its own feature matrix test so feature combinations 182 | # can be better controlled. 183 | do_feature_matrix() { 184 | # For crates that have unusual feature requirements (e.g. `corepc`). 185 | if [[ -v EXACT_FEATURES && ${#EXACT_FEATURES[@]} -gt 0 ]]; then 186 | for features in "${EXACT_FEATURES[@]}"; do 187 | $cargo build --no-default-features --features="$features" 188 | $cargo test --no-default-features --features="$features" 189 | done 190 | # rust-miniscript only: https://github.com/rust-bitcoin/rust-miniscript/issues/681 191 | elif [ -n "${FEATURES_WITH_NO_STD}" ]; then 192 | $cargo build --no-default-features --features="no-std" 193 | $cargo test --no-default-features --features="no-std" 194 | 195 | loop_features "no-std" "${FEATURES_WITH_NO_STD}" 196 | else 197 | $cargo build --no-default-features 198 | $cargo test --no-default-features 199 | fi 200 | 201 | $cargo build --all-features 202 | $cargo test --all-features 203 | 204 | if [ -n "${FEATURES_WITH_STD}" ]; then 205 | loop_features "std" "${FEATURES_WITH_STD}" 206 | fi 207 | 208 | if [ -n "${FEATURES_WITHOUT_STD}" ]; then 209 | loop_features "" "$FEATURES_WITHOUT_STD" 210 | fi 211 | } 212 | 213 | # Build with each feature as well as all combinations of two features. 214 | # 215 | # Usage: loop_features "std" "this-feature that-feature other" 216 | loop_features() { 217 | local use="${1:-}" # Allow empty string. 218 | local features="$2" # But require features. 219 | 220 | # All the provided features including $use 221 | $cargo build --no-default-features --features="$use $features" 222 | $cargo test --no-default-features --features="$use $features" 223 | 224 | read -r -a array <<< "$features" 225 | local len="${#array[@]}" 226 | 227 | if (( len > 1 )); then 228 | for ((i = 0 ; i < len ; i++ )); 229 | do 230 | $cargo build --no-default-features --features="$use ${array[i]}" 231 | $cargo test --no-default-features --features="$use ${array[i]}" 232 | 233 | if (( i < len - 1 )); then 234 | for ((j = i + 1 ; j < len ; j++ )); 235 | do 236 | $cargo build --no-default-features --features="$use ${array[i]} ${array[j]}" 237 | $cargo test --no-default-features --features="$use ${array[i]} ${array[j]}" 238 | done 239 | fi 240 | done 241 | fi 242 | } 243 | 244 | # Lint the workspace. 245 | do_lint_workspace() { 246 | need_nightly 247 | $cargo clippy --workspace --all-targets --all-features --keep-going -- -D warnings 248 | $cargo clippy --workspace --all-targets --keep-going -- -D warnings 249 | } 250 | 251 | # Run extra crate specific lints, e.g. clippy with no-default-features. 252 | do_lint_crates() { 253 | need_nightly 254 | for crate in $CRATES; do 255 | pushd "$REPO_DIR/$crate" > /dev/null 256 | if [ -e ./contrib/extra_lints.sh ]; then 257 | . ./contrib/extra_lints.sh 258 | fi 259 | popd > /dev/null 260 | done 261 | } 262 | 263 | # We should not have any duplicate dependencies. This catches mistakes made upgrading dependencies 264 | # in one crate and not in another (e.g. upgrade bitcoin_hashes in bitcoin but not in secp). 265 | do_dup_deps() { 266 | # We can't use pipefail because these grep statements fail by design when there is no duplicate, 267 | # the shell therefore won't pick up mistakes in your pipe - you are on your own. 268 | set +o pipefail 269 | 270 | # Contains dependencies that are expected to be duplicates. 271 | local duplicate_deps_script="$REPO_DIR/contrib/whitelist_deps.sh" 272 | 273 | # Only show the actual duplicated deps, not their reverse tree, then 274 | # whitelist the 'syn' crate which is duplicated but it's not our fault. 275 | local tree_cmd="cargo tree --target=all --all-features --duplicates \ 276 | | grep '^[0-9A-Za-z]' \ 277 | | grep -v 'syn'" 278 | 279 | # Add any duplicate dependencies to ignore. 280 | if [ -e "$duplicate_deps_script" ]; then 281 | verbose_say "Sourcing $duplicate_deps_script" 282 | # can't find the file because of the ENV var 283 | # shellcheck source=/dev/null 284 | . "$duplicate_deps_script" 285 | 286 | if [ -n "${DUPLICATE_DEPS+x}" ]; then 287 | for dep in "${DUPLICATE_DEPS[@]}"; do 288 | tree_cmd+=" | grep -v $dep" 289 | done 290 | else 291 | err "parsed $duplicate_deps_script but failed to find DUPLICATE_DEPS array" 292 | fi 293 | fi 294 | 295 | tree_cmd+="| wc -l" 296 | 297 | duplicate_dependencies=$(eval "$tree_cmd") 298 | 299 | if [ "$duplicate_dependencies" -ne 0 ]; then 300 | cargo tree --target=all --all-features --duplicates 301 | err "Dependency tree is broken, contains duplicates" 302 | fi 303 | 304 | set -o pipefail 305 | } 306 | 307 | # Build the docs with a nightly toolchain, in unison with the function 308 | # below this checks that we feature guarded docs imports correctly. 309 | build_docs_with_nightly_toolchain() { 310 | need_nightly 311 | # -j1 is because docs build fails if multiple versions of `bitcoin_hashes` are present in dep tree. 312 | RUSTDOCFLAGS="--cfg docsrs -D warnings -D rustdoc::broken-intra-doc-links" $cargo doc --all-features -j1 313 | } 314 | 315 | # Build the docs with a stable toolchain, in unison with the function 316 | # above this checks that we feature guarded docs imports correctly. 317 | build_docs_with_stable_toolchain() { 318 | local cargo="cargo +stable --locked" # Can't use global because of `+stable`. 319 | RUSTDOCFLAGS="-D warnings" $cargo doc --all-features -j1 320 | } 321 | 322 | # Bench only works with a non-stable toolchain (nightly, beta). 323 | do_bench() { 324 | verbose_say "Running bench tests for: $CRATES" 325 | 326 | for crate in $CRATES; do 327 | pushd "$REPO_DIR/$crate" > /dev/null 328 | # Unit tests are ignored so if there are no bench test then this will just succeed. 329 | RUSTFLAGS='--cfg=bench' cargo bench 330 | popd > /dev/null 331 | done 332 | } 333 | 334 | # Check all the commands we use are present in the current environment. 335 | check_required_commands() { 336 | need_cmd cargo 337 | need_cmd rustc 338 | need_cmd jq 339 | need_cmd cut 340 | need_cmd grep 341 | need_cmd wc 342 | } 343 | 344 | say() { 345 | echo "run_task: $1" 346 | } 347 | 348 | verbose_say() { 349 | case "${MAINTAINER_TOOLS_LOG_LEVEL:-verbose}" in 350 | quiet) 351 | # Suppress verbose output. 352 | ;; 353 | *) 354 | say "$1" 355 | ;; 356 | esac 357 | } 358 | 359 | err() { 360 | echo "$1" >&2 361 | exit 1 362 | } 363 | 364 | need_cmd() { 365 | if ! command -v "$1" > /dev/null 2>&1 366 | then err "need '$1' (command not found)" 367 | fi 368 | } 369 | 370 | need_nightly() { 371 | cargo_ver=$(cargo --version) 372 | if echo "$cargo_ver" | grep -q -v nightly; then 373 | err "Need a nightly compiler; have $(cargo --version)" 374 | fi 375 | } 376 | 377 | # 378 | # Main script 379 | # 380 | main "$@" 381 | exit 0 382 | 383 | -------------------------------------------------------------------------------- /releases/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.21.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler" 16 | version = "1.0.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 19 | 20 | [[package]] 21 | name = "anstream" 22 | version = "0.6.13" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" 25 | dependencies = [ 26 | "anstyle", 27 | "anstyle-parse", 28 | "anstyle-query", 29 | "anstyle-wincon", 30 | "colorchoice", 31 | "utf8parse", 32 | ] 33 | 34 | [[package]] 35 | name = "anstyle" 36 | version = "1.0.6" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" 39 | 40 | [[package]] 41 | name = "anstyle-parse" 42 | version = "0.2.3" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" 45 | dependencies = [ 46 | "utf8parse", 47 | ] 48 | 49 | [[package]] 50 | name = "anstyle-query" 51 | version = "1.0.2" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" 54 | dependencies = [ 55 | "windows-sys 0.52.0", 56 | ] 57 | 58 | [[package]] 59 | name = "anstyle-wincon" 60 | version = "3.0.2" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" 63 | dependencies = [ 64 | "anstyle", 65 | "windows-sys 0.52.0", 66 | ] 67 | 68 | [[package]] 69 | name = "anyhow" 70 | version = "1.0.82" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" 73 | 74 | [[package]] 75 | name = "autocfg" 76 | version = "1.2.0" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" 79 | 80 | [[package]] 81 | name = "backtrace" 82 | version = "0.3.71" 83 | source = "registry+https://github.com/rust-lang/crates.io-index" 84 | checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" 85 | dependencies = [ 86 | "addr2line", 87 | "cc", 88 | "cfg-if", 89 | "libc", 90 | "miniz_oxide", 91 | "object", 92 | "rustc-demangle", 93 | ] 94 | 95 | [[package]] 96 | name = "base64" 97 | version = "0.22.0" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51" 100 | 101 | [[package]] 102 | name = "bumpalo" 103 | version = "3.16.0" 104 | source = "registry+https://github.com/rust-lang/crates.io-index" 105 | checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" 106 | 107 | [[package]] 108 | name = "bytes" 109 | version = "1.6.0" 110 | source = "registry+https://github.com/rust-lang/crates.io-index" 111 | checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" 112 | 113 | [[package]] 114 | name = "cc" 115 | version = "1.0.95" 116 | source = "registry+https://github.com/rust-lang/crates.io-index" 117 | checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b" 118 | 119 | [[package]] 120 | name = "cfg-if" 121 | version = "1.0.0" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 124 | 125 | [[package]] 126 | name = "chrono" 127 | version = "0.4.38" 128 | source = "registry+https://github.com/rust-lang/crates.io-index" 129 | checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" 130 | dependencies = [ 131 | "num-traits", 132 | "serde", 133 | ] 134 | 135 | [[package]] 136 | name = "clap" 137 | version = "4.5.4" 138 | source = "registry+https://github.com/rust-lang/crates.io-index" 139 | checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" 140 | dependencies = [ 141 | "clap_builder", 142 | "clap_derive", 143 | ] 144 | 145 | [[package]] 146 | name = "clap_builder" 147 | version = "4.5.2" 148 | source = "registry+https://github.com/rust-lang/crates.io-index" 149 | checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" 150 | dependencies = [ 151 | "anstream", 152 | "anstyle", 153 | "clap_lex", 154 | "strsim", 155 | ] 156 | 157 | [[package]] 158 | name = "clap_derive" 159 | version = "4.5.4" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" 162 | dependencies = [ 163 | "heck", 164 | "proc-macro2", 165 | "quote", 166 | "syn", 167 | ] 168 | 169 | [[package]] 170 | name = "clap_lex" 171 | version = "0.7.0" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" 174 | 175 | [[package]] 176 | name = "colorchoice" 177 | version = "1.0.0" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" 180 | 181 | [[package]] 182 | name = "crates_io_api" 183 | version = "0.11.0" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | checksum = "200ad30d24892baf2168f2df366939264d02f2fa0be0914f8e2da4bd3407c58c" 186 | dependencies = [ 187 | "chrono", 188 | "futures", 189 | "reqwest", 190 | "serde", 191 | "serde_derive", 192 | "serde_json", 193 | "serde_path_to_error", 194 | "tokio", 195 | "url", 196 | ] 197 | 198 | [[package]] 199 | name = "equivalent" 200 | version = "1.0.1" 201 | source = "registry+https://github.com/rust-lang/crates.io-index" 202 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 203 | 204 | [[package]] 205 | name = "fnv" 206 | version = "1.0.7" 207 | source = "registry+https://github.com/rust-lang/crates.io-index" 208 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 209 | 210 | [[package]] 211 | name = "form_urlencoded" 212 | version = "1.2.1" 213 | source = "registry+https://github.com/rust-lang/crates.io-index" 214 | checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 215 | dependencies = [ 216 | "percent-encoding", 217 | ] 218 | 219 | [[package]] 220 | name = "futures" 221 | version = "0.3.30" 222 | source = "registry+https://github.com/rust-lang/crates.io-index" 223 | checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" 224 | dependencies = [ 225 | "futures-channel", 226 | "futures-core", 227 | "futures-executor", 228 | "futures-io", 229 | "futures-sink", 230 | "futures-task", 231 | "futures-util", 232 | ] 233 | 234 | [[package]] 235 | name = "futures-channel" 236 | version = "0.3.30" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" 239 | dependencies = [ 240 | "futures-core", 241 | "futures-sink", 242 | ] 243 | 244 | [[package]] 245 | name = "futures-core" 246 | version = "0.3.30" 247 | source = "registry+https://github.com/rust-lang/crates.io-index" 248 | checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" 249 | 250 | [[package]] 251 | name = "futures-executor" 252 | version = "0.3.30" 253 | source = "registry+https://github.com/rust-lang/crates.io-index" 254 | checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" 255 | dependencies = [ 256 | "futures-core", 257 | "futures-task", 258 | "futures-util", 259 | ] 260 | 261 | [[package]] 262 | name = "futures-io" 263 | version = "0.3.30" 264 | source = "registry+https://github.com/rust-lang/crates.io-index" 265 | checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" 266 | 267 | [[package]] 268 | name = "futures-macro" 269 | version = "0.3.30" 270 | source = "registry+https://github.com/rust-lang/crates.io-index" 271 | checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" 272 | dependencies = [ 273 | "proc-macro2", 274 | "quote", 275 | "syn", 276 | ] 277 | 278 | [[package]] 279 | name = "futures-sink" 280 | version = "0.3.30" 281 | source = "registry+https://github.com/rust-lang/crates.io-index" 282 | checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" 283 | 284 | [[package]] 285 | name = "futures-task" 286 | version = "0.3.30" 287 | source = "registry+https://github.com/rust-lang/crates.io-index" 288 | checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" 289 | 290 | [[package]] 291 | name = "futures-util" 292 | version = "0.3.30" 293 | source = "registry+https://github.com/rust-lang/crates.io-index" 294 | checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" 295 | dependencies = [ 296 | "futures-channel", 297 | "futures-core", 298 | "futures-io", 299 | "futures-macro", 300 | "futures-sink", 301 | "futures-task", 302 | "memchr", 303 | "pin-project-lite", 304 | "pin-utils", 305 | "slab", 306 | ] 307 | 308 | [[package]] 309 | name = "getrandom" 310 | version = "0.2.14" 311 | source = "registry+https://github.com/rust-lang/crates.io-index" 312 | checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" 313 | dependencies = [ 314 | "cfg-if", 315 | "libc", 316 | "wasi", 317 | ] 318 | 319 | [[package]] 320 | name = "gimli" 321 | version = "0.28.1" 322 | source = "registry+https://github.com/rust-lang/crates.io-index" 323 | checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" 324 | 325 | [[package]] 326 | name = "hashbrown" 327 | version = "0.14.3" 328 | source = "registry+https://github.com/rust-lang/crates.io-index" 329 | checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" 330 | 331 | [[package]] 332 | name = "heck" 333 | version = "0.5.0" 334 | source = "registry+https://github.com/rust-lang/crates.io-index" 335 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 336 | 337 | [[package]] 338 | name = "hermit-abi" 339 | version = "0.3.9" 340 | source = "registry+https://github.com/rust-lang/crates.io-index" 341 | checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" 342 | 343 | [[package]] 344 | name = "http" 345 | version = "1.1.0" 346 | source = "registry+https://github.com/rust-lang/crates.io-index" 347 | checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" 348 | dependencies = [ 349 | "bytes", 350 | "fnv", 351 | "itoa", 352 | ] 353 | 354 | [[package]] 355 | name = "http-body" 356 | version = "1.0.0" 357 | source = "registry+https://github.com/rust-lang/crates.io-index" 358 | checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" 359 | dependencies = [ 360 | "bytes", 361 | "http", 362 | ] 363 | 364 | [[package]] 365 | name = "http-body-util" 366 | version = "0.1.1" 367 | source = "registry+https://github.com/rust-lang/crates.io-index" 368 | checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" 369 | dependencies = [ 370 | "bytes", 371 | "futures-core", 372 | "http", 373 | "http-body", 374 | "pin-project-lite", 375 | ] 376 | 377 | [[package]] 378 | name = "httparse" 379 | version = "1.8.0" 380 | source = "registry+https://github.com/rust-lang/crates.io-index" 381 | checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" 382 | 383 | [[package]] 384 | name = "hyper" 385 | version = "1.3.1" 386 | source = "registry+https://github.com/rust-lang/crates.io-index" 387 | checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d" 388 | dependencies = [ 389 | "bytes", 390 | "futures-channel", 391 | "futures-util", 392 | "http", 393 | "http-body", 394 | "httparse", 395 | "itoa", 396 | "pin-project-lite", 397 | "smallvec", 398 | "tokio", 399 | "want", 400 | ] 401 | 402 | [[package]] 403 | name = "hyper-rustls" 404 | version = "0.26.0" 405 | source = "registry+https://github.com/rust-lang/crates.io-index" 406 | checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c" 407 | dependencies = [ 408 | "futures-util", 409 | "http", 410 | "hyper", 411 | "hyper-util", 412 | "rustls", 413 | "rustls-pki-types", 414 | "tokio", 415 | "tokio-rustls", 416 | "tower-service", 417 | ] 418 | 419 | [[package]] 420 | name = "hyper-util" 421 | version = "0.1.3" 422 | source = "registry+https://github.com/rust-lang/crates.io-index" 423 | checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" 424 | dependencies = [ 425 | "bytes", 426 | "futures-channel", 427 | "futures-util", 428 | "http", 429 | "http-body", 430 | "hyper", 431 | "pin-project-lite", 432 | "socket2", 433 | "tokio", 434 | "tower", 435 | "tower-service", 436 | "tracing", 437 | ] 438 | 439 | [[package]] 440 | name = "idna" 441 | version = "0.5.0" 442 | source = "registry+https://github.com/rust-lang/crates.io-index" 443 | checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" 444 | dependencies = [ 445 | "unicode-bidi", 446 | "unicode-normalization", 447 | ] 448 | 449 | [[package]] 450 | name = "indexmap" 451 | version = "2.2.6" 452 | source = "registry+https://github.com/rust-lang/crates.io-index" 453 | checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" 454 | dependencies = [ 455 | "equivalent", 456 | "hashbrown", 457 | ] 458 | 459 | [[package]] 460 | name = "ipnet" 461 | version = "2.9.0" 462 | source = "registry+https://github.com/rust-lang/crates.io-index" 463 | checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" 464 | 465 | [[package]] 466 | name = "itoa" 467 | version = "1.0.11" 468 | source = "registry+https://github.com/rust-lang/crates.io-index" 469 | checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" 470 | 471 | [[package]] 472 | name = "js-sys" 473 | version = "0.3.69" 474 | source = "registry+https://github.com/rust-lang/crates.io-index" 475 | checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" 476 | dependencies = [ 477 | "wasm-bindgen", 478 | ] 479 | 480 | [[package]] 481 | name = "libc" 482 | version = "0.2.153" 483 | source = "registry+https://github.com/rust-lang/crates.io-index" 484 | checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" 485 | 486 | [[package]] 487 | name = "log" 488 | version = "0.4.21" 489 | source = "registry+https://github.com/rust-lang/crates.io-index" 490 | checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" 491 | 492 | [[package]] 493 | name = "memchr" 494 | version = "2.7.2" 495 | source = "registry+https://github.com/rust-lang/crates.io-index" 496 | checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" 497 | 498 | [[package]] 499 | name = "mime" 500 | version = "0.3.17" 501 | source = "registry+https://github.com/rust-lang/crates.io-index" 502 | checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" 503 | 504 | [[package]] 505 | name = "miniz_oxide" 506 | version = "0.7.2" 507 | source = "registry+https://github.com/rust-lang/crates.io-index" 508 | checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" 509 | dependencies = [ 510 | "adler", 511 | ] 512 | 513 | [[package]] 514 | name = "mio" 515 | version = "0.8.11" 516 | source = "registry+https://github.com/rust-lang/crates.io-index" 517 | checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" 518 | dependencies = [ 519 | "libc", 520 | "wasi", 521 | "windows-sys 0.48.0", 522 | ] 523 | 524 | [[package]] 525 | name = "num-traits" 526 | version = "0.2.18" 527 | source = "registry+https://github.com/rust-lang/crates.io-index" 528 | checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" 529 | dependencies = [ 530 | "autocfg", 531 | ] 532 | 533 | [[package]] 534 | name = "num_cpus" 535 | version = "1.16.0" 536 | source = "registry+https://github.com/rust-lang/crates.io-index" 537 | checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" 538 | dependencies = [ 539 | "hermit-abi", 540 | "libc", 541 | ] 542 | 543 | [[package]] 544 | name = "object" 545 | version = "0.32.2" 546 | source = "registry+https://github.com/rust-lang/crates.io-index" 547 | checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" 548 | dependencies = [ 549 | "memchr", 550 | ] 551 | 552 | [[package]] 553 | name = "once_cell" 554 | version = "1.19.0" 555 | source = "registry+https://github.com/rust-lang/crates.io-index" 556 | checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" 557 | 558 | [[package]] 559 | name = "percent-encoding" 560 | version = "2.3.1" 561 | source = "registry+https://github.com/rust-lang/crates.io-index" 562 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 563 | 564 | [[package]] 565 | name = "pin-project" 566 | version = "1.1.5" 567 | source = "registry+https://github.com/rust-lang/crates.io-index" 568 | checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" 569 | dependencies = [ 570 | "pin-project-internal", 571 | ] 572 | 573 | [[package]] 574 | name = "pin-project-internal" 575 | version = "1.1.5" 576 | source = "registry+https://github.com/rust-lang/crates.io-index" 577 | checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" 578 | dependencies = [ 579 | "proc-macro2", 580 | "quote", 581 | "syn", 582 | ] 583 | 584 | [[package]] 585 | name = "pin-project-lite" 586 | version = "0.2.14" 587 | source = "registry+https://github.com/rust-lang/crates.io-index" 588 | checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" 589 | 590 | [[package]] 591 | name = "pin-utils" 592 | version = "0.1.0" 593 | source = "registry+https://github.com/rust-lang/crates.io-index" 594 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 595 | 596 | [[package]] 597 | name = "proc-macro2" 598 | version = "1.0.81" 599 | source = "registry+https://github.com/rust-lang/crates.io-index" 600 | checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" 601 | dependencies = [ 602 | "unicode-ident", 603 | ] 604 | 605 | [[package]] 606 | name = "quote" 607 | version = "1.0.36" 608 | source = "registry+https://github.com/rust-lang/crates.io-index" 609 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 610 | dependencies = [ 611 | "proc-macro2", 612 | ] 613 | 614 | [[package]] 615 | name = "releases" 616 | version = "0.1.0" 617 | dependencies = [ 618 | "anyhow", 619 | "clap", 620 | "crates_io_api", 621 | "semver", 622 | "serde", 623 | "serde_json", 624 | "tokio", 625 | "toml", 626 | ] 627 | 628 | [[package]] 629 | name = "reqwest" 630 | version = "0.12.4" 631 | source = "registry+https://github.com/rust-lang/crates.io-index" 632 | checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" 633 | dependencies = [ 634 | "base64", 635 | "bytes", 636 | "futures-channel", 637 | "futures-core", 638 | "futures-util", 639 | "http", 640 | "http-body", 641 | "http-body-util", 642 | "hyper", 643 | "hyper-rustls", 644 | "hyper-util", 645 | "ipnet", 646 | "js-sys", 647 | "log", 648 | "mime", 649 | "once_cell", 650 | "percent-encoding", 651 | "pin-project-lite", 652 | "rustls", 653 | "rustls-pemfile", 654 | "rustls-pki-types", 655 | "serde", 656 | "serde_json", 657 | "serde_urlencoded", 658 | "sync_wrapper", 659 | "tokio", 660 | "tokio-rustls", 661 | "tower-service", 662 | "url", 663 | "wasm-bindgen", 664 | "wasm-bindgen-futures", 665 | "web-sys", 666 | "webpki-roots", 667 | "winreg", 668 | ] 669 | 670 | [[package]] 671 | name = "ring" 672 | version = "0.17.8" 673 | source = "registry+https://github.com/rust-lang/crates.io-index" 674 | checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" 675 | dependencies = [ 676 | "cc", 677 | "cfg-if", 678 | "getrandom", 679 | "libc", 680 | "spin", 681 | "untrusted", 682 | "windows-sys 0.52.0", 683 | ] 684 | 685 | [[package]] 686 | name = "rustc-demangle" 687 | version = "0.1.23" 688 | source = "registry+https://github.com/rust-lang/crates.io-index" 689 | checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" 690 | 691 | [[package]] 692 | name = "rustls" 693 | version = "0.22.4" 694 | source = "registry+https://github.com/rust-lang/crates.io-index" 695 | checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" 696 | dependencies = [ 697 | "log", 698 | "ring", 699 | "rustls-pki-types", 700 | "rustls-webpki", 701 | "subtle", 702 | "zeroize", 703 | ] 704 | 705 | [[package]] 706 | name = "rustls-pemfile" 707 | version = "2.1.2" 708 | source = "registry+https://github.com/rust-lang/crates.io-index" 709 | checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" 710 | dependencies = [ 711 | "base64", 712 | "rustls-pki-types", 713 | ] 714 | 715 | [[package]] 716 | name = "rustls-pki-types" 717 | version = "1.5.0" 718 | source = "registry+https://github.com/rust-lang/crates.io-index" 719 | checksum = "beb461507cee2c2ff151784c52762cf4d9ff6a61f3e80968600ed24fa837fa54" 720 | 721 | [[package]] 722 | name = "rustls-webpki" 723 | version = "0.102.3" 724 | source = "registry+https://github.com/rust-lang/crates.io-index" 725 | checksum = "f3bce581c0dd41bce533ce695a1437fa16a7ab5ac3ccfa99fe1a620a7885eabf" 726 | dependencies = [ 727 | "ring", 728 | "rustls-pki-types", 729 | "untrusted", 730 | ] 731 | 732 | [[package]] 733 | name = "ryu" 734 | version = "1.0.17" 735 | source = "registry+https://github.com/rust-lang/crates.io-index" 736 | checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" 737 | 738 | [[package]] 739 | name = "semver" 740 | version = "1.0.22" 741 | source = "registry+https://github.com/rust-lang/crates.io-index" 742 | checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" 743 | 744 | [[package]] 745 | name = "serde" 746 | version = "1.0.198" 747 | source = "registry+https://github.com/rust-lang/crates.io-index" 748 | checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" 749 | dependencies = [ 750 | "serde_derive", 751 | ] 752 | 753 | [[package]] 754 | name = "serde_derive" 755 | version = "1.0.198" 756 | source = "registry+https://github.com/rust-lang/crates.io-index" 757 | checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" 758 | dependencies = [ 759 | "proc-macro2", 760 | "quote", 761 | "syn", 762 | ] 763 | 764 | [[package]] 765 | name = "serde_json" 766 | version = "1.0.116" 767 | source = "registry+https://github.com/rust-lang/crates.io-index" 768 | checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" 769 | dependencies = [ 770 | "itoa", 771 | "ryu", 772 | "serde", 773 | ] 774 | 775 | [[package]] 776 | name = "serde_path_to_error" 777 | version = "0.1.16" 778 | source = "registry+https://github.com/rust-lang/crates.io-index" 779 | checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" 780 | dependencies = [ 781 | "itoa", 782 | "serde", 783 | ] 784 | 785 | [[package]] 786 | name = "serde_spanned" 787 | version = "0.6.5" 788 | source = "registry+https://github.com/rust-lang/crates.io-index" 789 | checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" 790 | dependencies = [ 791 | "serde", 792 | ] 793 | 794 | [[package]] 795 | name = "serde_urlencoded" 796 | version = "0.7.1" 797 | source = "registry+https://github.com/rust-lang/crates.io-index" 798 | checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 799 | dependencies = [ 800 | "form_urlencoded", 801 | "itoa", 802 | "ryu", 803 | "serde", 804 | ] 805 | 806 | [[package]] 807 | name = "slab" 808 | version = "0.4.9" 809 | source = "registry+https://github.com/rust-lang/crates.io-index" 810 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 811 | dependencies = [ 812 | "autocfg", 813 | ] 814 | 815 | [[package]] 816 | name = "smallvec" 817 | version = "1.13.2" 818 | source = "registry+https://github.com/rust-lang/crates.io-index" 819 | checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 820 | 821 | [[package]] 822 | name = "socket2" 823 | version = "0.5.6" 824 | source = "registry+https://github.com/rust-lang/crates.io-index" 825 | checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" 826 | dependencies = [ 827 | "libc", 828 | "windows-sys 0.52.0", 829 | ] 830 | 831 | [[package]] 832 | name = "spin" 833 | version = "0.9.8" 834 | source = "registry+https://github.com/rust-lang/crates.io-index" 835 | checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" 836 | 837 | [[package]] 838 | name = "strsim" 839 | version = "0.11.1" 840 | source = "registry+https://github.com/rust-lang/crates.io-index" 841 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 842 | 843 | [[package]] 844 | name = "subtle" 845 | version = "2.5.0" 846 | source = "registry+https://github.com/rust-lang/crates.io-index" 847 | checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" 848 | 849 | [[package]] 850 | name = "syn" 851 | version = "2.0.60" 852 | source = "registry+https://github.com/rust-lang/crates.io-index" 853 | checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" 854 | dependencies = [ 855 | "proc-macro2", 856 | "quote", 857 | "unicode-ident", 858 | ] 859 | 860 | [[package]] 861 | name = "sync_wrapper" 862 | version = "0.1.2" 863 | source = "registry+https://github.com/rust-lang/crates.io-index" 864 | checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" 865 | 866 | [[package]] 867 | name = "tinyvec" 868 | version = "1.6.0" 869 | source = "registry+https://github.com/rust-lang/crates.io-index" 870 | checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" 871 | dependencies = [ 872 | "tinyvec_macros", 873 | ] 874 | 875 | [[package]] 876 | name = "tinyvec_macros" 877 | version = "0.1.1" 878 | source = "registry+https://github.com/rust-lang/crates.io-index" 879 | checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" 880 | 881 | [[package]] 882 | name = "tokio" 883 | version = "1.37.0" 884 | source = "registry+https://github.com/rust-lang/crates.io-index" 885 | checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" 886 | dependencies = [ 887 | "backtrace", 888 | "bytes", 889 | "libc", 890 | "mio", 891 | "num_cpus", 892 | "pin-project-lite", 893 | "socket2", 894 | "tokio-macros", 895 | "windows-sys 0.48.0", 896 | ] 897 | 898 | [[package]] 899 | name = "tokio-macros" 900 | version = "2.2.0" 901 | source = "registry+https://github.com/rust-lang/crates.io-index" 902 | checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" 903 | dependencies = [ 904 | "proc-macro2", 905 | "quote", 906 | "syn", 907 | ] 908 | 909 | [[package]] 910 | name = "tokio-rustls" 911 | version = "0.25.0" 912 | source = "registry+https://github.com/rust-lang/crates.io-index" 913 | checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" 914 | dependencies = [ 915 | "rustls", 916 | "rustls-pki-types", 917 | "tokio", 918 | ] 919 | 920 | [[package]] 921 | name = "toml" 922 | version = "0.8.12" 923 | source = "registry+https://github.com/rust-lang/crates.io-index" 924 | checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" 925 | dependencies = [ 926 | "serde", 927 | "serde_spanned", 928 | "toml_datetime", 929 | "toml_edit", 930 | ] 931 | 932 | [[package]] 933 | name = "toml_datetime" 934 | version = "0.6.5" 935 | source = "registry+https://github.com/rust-lang/crates.io-index" 936 | checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" 937 | dependencies = [ 938 | "serde", 939 | ] 940 | 941 | [[package]] 942 | name = "toml_edit" 943 | version = "0.22.12" 944 | source = "registry+https://github.com/rust-lang/crates.io-index" 945 | checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" 946 | dependencies = [ 947 | "indexmap", 948 | "serde", 949 | "serde_spanned", 950 | "toml_datetime", 951 | "winnow", 952 | ] 953 | 954 | [[package]] 955 | name = "tower" 956 | version = "0.4.13" 957 | source = "registry+https://github.com/rust-lang/crates.io-index" 958 | checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" 959 | dependencies = [ 960 | "futures-core", 961 | "futures-util", 962 | "pin-project", 963 | "pin-project-lite", 964 | "tokio", 965 | "tower-layer", 966 | "tower-service", 967 | "tracing", 968 | ] 969 | 970 | [[package]] 971 | name = "tower-layer" 972 | version = "0.3.2" 973 | source = "registry+https://github.com/rust-lang/crates.io-index" 974 | checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" 975 | 976 | [[package]] 977 | name = "tower-service" 978 | version = "0.3.2" 979 | source = "registry+https://github.com/rust-lang/crates.io-index" 980 | checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" 981 | 982 | [[package]] 983 | name = "tracing" 984 | version = "0.1.40" 985 | source = "registry+https://github.com/rust-lang/crates.io-index" 986 | checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" 987 | dependencies = [ 988 | "log", 989 | "pin-project-lite", 990 | "tracing-core", 991 | ] 992 | 993 | [[package]] 994 | name = "tracing-core" 995 | version = "0.1.32" 996 | source = "registry+https://github.com/rust-lang/crates.io-index" 997 | checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" 998 | dependencies = [ 999 | "once_cell", 1000 | ] 1001 | 1002 | [[package]] 1003 | name = "try-lock" 1004 | version = "0.2.5" 1005 | source = "registry+https://github.com/rust-lang/crates.io-index" 1006 | checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" 1007 | 1008 | [[package]] 1009 | name = "unicode-bidi" 1010 | version = "0.3.15" 1011 | source = "registry+https://github.com/rust-lang/crates.io-index" 1012 | checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" 1013 | 1014 | [[package]] 1015 | name = "unicode-ident" 1016 | version = "1.0.12" 1017 | source = "registry+https://github.com/rust-lang/crates.io-index" 1018 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 1019 | 1020 | [[package]] 1021 | name = "unicode-normalization" 1022 | version = "0.1.23" 1023 | source = "registry+https://github.com/rust-lang/crates.io-index" 1024 | checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" 1025 | dependencies = [ 1026 | "tinyvec", 1027 | ] 1028 | 1029 | [[package]] 1030 | name = "untrusted" 1031 | version = "0.9.0" 1032 | source = "registry+https://github.com/rust-lang/crates.io-index" 1033 | checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" 1034 | 1035 | [[package]] 1036 | name = "url" 1037 | version = "2.5.0" 1038 | source = "registry+https://github.com/rust-lang/crates.io-index" 1039 | checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" 1040 | dependencies = [ 1041 | "form_urlencoded", 1042 | "idna", 1043 | "percent-encoding", 1044 | ] 1045 | 1046 | [[package]] 1047 | name = "utf8parse" 1048 | version = "0.2.1" 1049 | source = "registry+https://github.com/rust-lang/crates.io-index" 1050 | checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" 1051 | 1052 | [[package]] 1053 | name = "want" 1054 | version = "0.3.1" 1055 | source = "registry+https://github.com/rust-lang/crates.io-index" 1056 | checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" 1057 | dependencies = [ 1058 | "try-lock", 1059 | ] 1060 | 1061 | [[package]] 1062 | name = "wasi" 1063 | version = "0.11.0+wasi-snapshot-preview1" 1064 | source = "registry+https://github.com/rust-lang/crates.io-index" 1065 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1066 | 1067 | [[package]] 1068 | name = "wasm-bindgen" 1069 | version = "0.2.92" 1070 | source = "registry+https://github.com/rust-lang/crates.io-index" 1071 | checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" 1072 | dependencies = [ 1073 | "cfg-if", 1074 | "wasm-bindgen-macro", 1075 | ] 1076 | 1077 | [[package]] 1078 | name = "wasm-bindgen-backend" 1079 | version = "0.2.92" 1080 | source = "registry+https://github.com/rust-lang/crates.io-index" 1081 | checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" 1082 | dependencies = [ 1083 | "bumpalo", 1084 | "log", 1085 | "once_cell", 1086 | "proc-macro2", 1087 | "quote", 1088 | "syn", 1089 | "wasm-bindgen-shared", 1090 | ] 1091 | 1092 | [[package]] 1093 | name = "wasm-bindgen-futures" 1094 | version = "0.4.42" 1095 | source = "registry+https://github.com/rust-lang/crates.io-index" 1096 | checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" 1097 | dependencies = [ 1098 | "cfg-if", 1099 | "js-sys", 1100 | "wasm-bindgen", 1101 | "web-sys", 1102 | ] 1103 | 1104 | [[package]] 1105 | name = "wasm-bindgen-macro" 1106 | version = "0.2.92" 1107 | source = "registry+https://github.com/rust-lang/crates.io-index" 1108 | checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" 1109 | dependencies = [ 1110 | "quote", 1111 | "wasm-bindgen-macro-support", 1112 | ] 1113 | 1114 | [[package]] 1115 | name = "wasm-bindgen-macro-support" 1116 | version = "0.2.92" 1117 | source = "registry+https://github.com/rust-lang/crates.io-index" 1118 | checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" 1119 | dependencies = [ 1120 | "proc-macro2", 1121 | "quote", 1122 | "syn", 1123 | "wasm-bindgen-backend", 1124 | "wasm-bindgen-shared", 1125 | ] 1126 | 1127 | [[package]] 1128 | name = "wasm-bindgen-shared" 1129 | version = "0.2.92" 1130 | source = "registry+https://github.com/rust-lang/crates.io-index" 1131 | checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" 1132 | 1133 | [[package]] 1134 | name = "web-sys" 1135 | version = "0.3.69" 1136 | source = "registry+https://github.com/rust-lang/crates.io-index" 1137 | checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" 1138 | dependencies = [ 1139 | "js-sys", 1140 | "wasm-bindgen", 1141 | ] 1142 | 1143 | [[package]] 1144 | name = "webpki-roots" 1145 | version = "0.26.1" 1146 | source = "registry+https://github.com/rust-lang/crates.io-index" 1147 | checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009" 1148 | dependencies = [ 1149 | "rustls-pki-types", 1150 | ] 1151 | 1152 | [[package]] 1153 | name = "windows-sys" 1154 | version = "0.48.0" 1155 | source = "registry+https://github.com/rust-lang/crates.io-index" 1156 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 1157 | dependencies = [ 1158 | "windows-targets 0.48.5", 1159 | ] 1160 | 1161 | [[package]] 1162 | name = "windows-sys" 1163 | version = "0.52.0" 1164 | source = "registry+https://github.com/rust-lang/crates.io-index" 1165 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 1166 | dependencies = [ 1167 | "windows-targets 0.52.5", 1168 | ] 1169 | 1170 | [[package]] 1171 | name = "windows-targets" 1172 | version = "0.48.5" 1173 | source = "registry+https://github.com/rust-lang/crates.io-index" 1174 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 1175 | dependencies = [ 1176 | "windows_aarch64_gnullvm 0.48.5", 1177 | "windows_aarch64_msvc 0.48.5", 1178 | "windows_i686_gnu 0.48.5", 1179 | "windows_i686_msvc 0.48.5", 1180 | "windows_x86_64_gnu 0.48.5", 1181 | "windows_x86_64_gnullvm 0.48.5", 1182 | "windows_x86_64_msvc 0.48.5", 1183 | ] 1184 | 1185 | [[package]] 1186 | name = "windows-targets" 1187 | version = "0.52.5" 1188 | source = "registry+https://github.com/rust-lang/crates.io-index" 1189 | checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" 1190 | dependencies = [ 1191 | "windows_aarch64_gnullvm 0.52.5", 1192 | "windows_aarch64_msvc 0.52.5", 1193 | "windows_i686_gnu 0.52.5", 1194 | "windows_i686_gnullvm", 1195 | "windows_i686_msvc 0.52.5", 1196 | "windows_x86_64_gnu 0.52.5", 1197 | "windows_x86_64_gnullvm 0.52.5", 1198 | "windows_x86_64_msvc 0.52.5", 1199 | ] 1200 | 1201 | [[package]] 1202 | name = "windows_aarch64_gnullvm" 1203 | version = "0.48.5" 1204 | source = "registry+https://github.com/rust-lang/crates.io-index" 1205 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 1206 | 1207 | [[package]] 1208 | name = "windows_aarch64_gnullvm" 1209 | version = "0.52.5" 1210 | source = "registry+https://github.com/rust-lang/crates.io-index" 1211 | checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" 1212 | 1213 | [[package]] 1214 | name = "windows_aarch64_msvc" 1215 | version = "0.48.5" 1216 | source = "registry+https://github.com/rust-lang/crates.io-index" 1217 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 1218 | 1219 | [[package]] 1220 | name = "windows_aarch64_msvc" 1221 | version = "0.52.5" 1222 | source = "registry+https://github.com/rust-lang/crates.io-index" 1223 | checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" 1224 | 1225 | [[package]] 1226 | name = "windows_i686_gnu" 1227 | version = "0.48.5" 1228 | source = "registry+https://github.com/rust-lang/crates.io-index" 1229 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 1230 | 1231 | [[package]] 1232 | name = "windows_i686_gnu" 1233 | version = "0.52.5" 1234 | source = "registry+https://github.com/rust-lang/crates.io-index" 1235 | checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" 1236 | 1237 | [[package]] 1238 | name = "windows_i686_gnullvm" 1239 | version = "0.52.5" 1240 | source = "registry+https://github.com/rust-lang/crates.io-index" 1241 | checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" 1242 | 1243 | [[package]] 1244 | name = "windows_i686_msvc" 1245 | version = "0.48.5" 1246 | source = "registry+https://github.com/rust-lang/crates.io-index" 1247 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 1248 | 1249 | [[package]] 1250 | name = "windows_i686_msvc" 1251 | version = "0.52.5" 1252 | source = "registry+https://github.com/rust-lang/crates.io-index" 1253 | checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" 1254 | 1255 | [[package]] 1256 | name = "windows_x86_64_gnu" 1257 | version = "0.48.5" 1258 | source = "registry+https://github.com/rust-lang/crates.io-index" 1259 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 1260 | 1261 | [[package]] 1262 | name = "windows_x86_64_gnu" 1263 | version = "0.52.5" 1264 | source = "registry+https://github.com/rust-lang/crates.io-index" 1265 | checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" 1266 | 1267 | [[package]] 1268 | name = "windows_x86_64_gnullvm" 1269 | version = "0.48.5" 1270 | source = "registry+https://github.com/rust-lang/crates.io-index" 1271 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 1272 | 1273 | [[package]] 1274 | name = "windows_x86_64_gnullvm" 1275 | version = "0.52.5" 1276 | source = "registry+https://github.com/rust-lang/crates.io-index" 1277 | checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" 1278 | 1279 | [[package]] 1280 | name = "windows_x86_64_msvc" 1281 | version = "0.48.5" 1282 | source = "registry+https://github.com/rust-lang/crates.io-index" 1283 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 1284 | 1285 | [[package]] 1286 | name = "windows_x86_64_msvc" 1287 | version = "0.52.5" 1288 | source = "registry+https://github.com/rust-lang/crates.io-index" 1289 | checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" 1290 | 1291 | [[package]] 1292 | name = "winnow" 1293 | version = "0.6.6" 1294 | source = "registry+https://github.com/rust-lang/crates.io-index" 1295 | checksum = "f0c976aaaa0e1f90dbb21e9587cdaf1d9679a1cde8875c0d6bd83ab96a208352" 1296 | dependencies = [ 1297 | "memchr", 1298 | ] 1299 | 1300 | [[package]] 1301 | name = "winreg" 1302 | version = "0.52.0" 1303 | source = "registry+https://github.com/rust-lang/crates.io-index" 1304 | checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" 1305 | dependencies = [ 1306 | "cfg-if", 1307 | "windows-sys 0.48.0", 1308 | ] 1309 | 1310 | [[package]] 1311 | name = "zeroize" 1312 | version = "1.7.0" 1313 | source = "registry+https://github.com/rust-lang/crates.io-index" 1314 | checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" 1315 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "anstream" 7 | version = "0.6.21" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" 10 | dependencies = [ 11 | "anstyle", 12 | "anstyle-parse", 13 | "anstyle-query", 14 | "anstyle-wincon", 15 | "colorchoice", 16 | "is_terminal_polyfill", 17 | "utf8parse", 18 | ] 19 | 20 | [[package]] 21 | name = "anstyle" 22 | version = "1.0.13" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" 25 | 26 | [[package]] 27 | name = "anstyle-parse" 28 | version = "0.2.7" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" 31 | dependencies = [ 32 | "utf8parse", 33 | ] 34 | 35 | [[package]] 36 | name = "anstyle-query" 37 | version = "1.1.4" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" 40 | dependencies = [ 41 | "windows-sys 0.60.2", 42 | ] 43 | 44 | [[package]] 45 | name = "anstyle-wincon" 46 | version = "3.0.10" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" 49 | dependencies = [ 50 | "anstyle", 51 | "once_cell_polyfill", 52 | "windows-sys 0.60.2", 53 | ] 54 | 55 | [[package]] 56 | name = "anyhow" 57 | version = "1.0.100" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" 60 | 61 | [[package]] 62 | name = "atomic-waker" 63 | version = "1.1.2" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" 66 | 67 | [[package]] 68 | name = "autocfg" 69 | version = "1.5.0" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" 72 | 73 | [[package]] 74 | name = "base64" 75 | version = "0.22.1" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 78 | 79 | [[package]] 80 | name = "bitflags" 81 | version = "2.10.0" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" 84 | 85 | [[package]] 86 | name = "bumpalo" 87 | version = "3.19.0" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" 90 | 91 | [[package]] 92 | name = "bytes" 93 | version = "1.10.1" 94 | source = "registry+https://github.com/rust-lang/crates.io-index" 95 | checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" 96 | 97 | [[package]] 98 | name = "cargo-rbmt" 99 | version = "0.1.0" 100 | dependencies = [ 101 | "clap", 102 | "serde", 103 | "serde_json", 104 | "toml", 105 | "xshell", 106 | ] 107 | 108 | [[package]] 109 | name = "cc" 110 | version = "1.2.44" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "37521ac7aabe3d13122dc382493e20c9416f299d2ccd5b3a5340a2570cdeb0f3" 113 | dependencies = [ 114 | "find-msvc-tools", 115 | "shlex", 116 | ] 117 | 118 | [[package]] 119 | name = "cfg-if" 120 | version = "1.0.4" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" 123 | 124 | [[package]] 125 | name = "cfg_aliases" 126 | version = "0.2.1" 127 | source = "registry+https://github.com/rust-lang/crates.io-index" 128 | checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" 129 | 130 | [[package]] 131 | name = "chrono" 132 | version = "0.4.42" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" 135 | dependencies = [ 136 | "num-traits", 137 | "serde", 138 | ] 139 | 140 | [[package]] 141 | name = "clap" 142 | version = "4.5.51" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5" 145 | dependencies = [ 146 | "clap_builder", 147 | "clap_derive", 148 | ] 149 | 150 | [[package]] 151 | name = "clap_builder" 152 | version = "4.5.51" 153 | source = "registry+https://github.com/rust-lang/crates.io-index" 154 | checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a" 155 | dependencies = [ 156 | "anstream", 157 | "anstyle", 158 | "clap_lex", 159 | "strsim", 160 | ] 161 | 162 | [[package]] 163 | name = "clap_derive" 164 | version = "4.5.49" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" 167 | dependencies = [ 168 | "heck", 169 | "proc-macro2", 170 | "quote", 171 | "syn", 172 | ] 173 | 174 | [[package]] 175 | name = "clap_lex" 176 | version = "0.7.6" 177 | source = "registry+https://github.com/rust-lang/crates.io-index" 178 | checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" 179 | 180 | [[package]] 181 | name = "colorchoice" 182 | version = "1.0.4" 183 | source = "registry+https://github.com/rust-lang/crates.io-index" 184 | checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" 185 | 186 | [[package]] 187 | name = "crates_io_api" 188 | version = "0.11.0" 189 | source = "registry+https://github.com/rust-lang/crates.io-index" 190 | checksum = "200ad30d24892baf2168f2df366939264d02f2fa0be0914f8e2da4bd3407c58c" 191 | dependencies = [ 192 | "chrono", 193 | "futures", 194 | "reqwest", 195 | "serde", 196 | "serde_derive", 197 | "serde_json", 198 | "serde_path_to_error", 199 | "tokio", 200 | "url", 201 | ] 202 | 203 | [[package]] 204 | name = "equivalent" 205 | version = "1.0.2" 206 | source = "registry+https://github.com/rust-lang/crates.io-index" 207 | checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" 208 | 209 | [[package]] 210 | name = "find-msvc-tools" 211 | version = "0.1.4" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" 214 | 215 | [[package]] 216 | name = "fnv" 217 | version = "1.0.7" 218 | source = "registry+https://github.com/rust-lang/crates.io-index" 219 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 220 | 221 | [[package]] 222 | name = "form_urlencoded" 223 | version = "1.2.2" 224 | source = "registry+https://github.com/rust-lang/crates.io-index" 225 | checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" 226 | dependencies = [ 227 | "percent-encoding", 228 | ] 229 | 230 | [[package]] 231 | name = "futures" 232 | version = "0.3.31" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" 235 | dependencies = [ 236 | "futures-channel", 237 | "futures-core", 238 | "futures-executor", 239 | "futures-io", 240 | "futures-sink", 241 | "futures-task", 242 | "futures-util", 243 | ] 244 | 245 | [[package]] 246 | name = "futures-channel" 247 | version = "0.3.31" 248 | source = "registry+https://github.com/rust-lang/crates.io-index" 249 | checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" 250 | dependencies = [ 251 | "futures-core", 252 | "futures-sink", 253 | ] 254 | 255 | [[package]] 256 | name = "futures-core" 257 | version = "0.3.31" 258 | source = "registry+https://github.com/rust-lang/crates.io-index" 259 | checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 260 | 261 | [[package]] 262 | name = "futures-executor" 263 | version = "0.3.31" 264 | source = "registry+https://github.com/rust-lang/crates.io-index" 265 | checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" 266 | dependencies = [ 267 | "futures-core", 268 | "futures-task", 269 | "futures-util", 270 | ] 271 | 272 | [[package]] 273 | name = "futures-io" 274 | version = "0.3.31" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" 277 | 278 | [[package]] 279 | name = "futures-macro" 280 | version = "0.3.31" 281 | source = "registry+https://github.com/rust-lang/crates.io-index" 282 | checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" 283 | dependencies = [ 284 | "proc-macro2", 285 | "quote", 286 | "syn", 287 | ] 288 | 289 | [[package]] 290 | name = "futures-sink" 291 | version = "0.3.31" 292 | source = "registry+https://github.com/rust-lang/crates.io-index" 293 | checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" 294 | 295 | [[package]] 296 | name = "futures-task" 297 | version = "0.3.31" 298 | source = "registry+https://github.com/rust-lang/crates.io-index" 299 | checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 300 | 301 | [[package]] 302 | name = "futures-util" 303 | version = "0.3.31" 304 | source = "registry+https://github.com/rust-lang/crates.io-index" 305 | checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 306 | dependencies = [ 307 | "futures-channel", 308 | "futures-core", 309 | "futures-io", 310 | "futures-macro", 311 | "futures-sink", 312 | "futures-task", 313 | "memchr", 314 | "pin-project-lite", 315 | "pin-utils", 316 | "slab", 317 | ] 318 | 319 | [[package]] 320 | name = "getrandom" 321 | version = "0.2.16" 322 | source = "registry+https://github.com/rust-lang/crates.io-index" 323 | checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" 324 | dependencies = [ 325 | "cfg-if", 326 | "js-sys", 327 | "libc", 328 | "wasi", 329 | "wasm-bindgen", 330 | ] 331 | 332 | [[package]] 333 | name = "getrandom" 334 | version = "0.3.4" 335 | source = "registry+https://github.com/rust-lang/crates.io-index" 336 | checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" 337 | dependencies = [ 338 | "cfg-if", 339 | "js-sys", 340 | "libc", 341 | "r-efi", 342 | "wasip2", 343 | "wasm-bindgen", 344 | ] 345 | 346 | [[package]] 347 | name = "hashbrown" 348 | version = "0.14.5" 349 | source = "registry+https://github.com/rust-lang/crates.io-index" 350 | checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" 351 | 352 | [[package]] 353 | name = "heck" 354 | version = "0.5.0" 355 | source = "registry+https://github.com/rust-lang/crates.io-index" 356 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 357 | 358 | [[package]] 359 | name = "http" 360 | version = "1.3.1" 361 | source = "registry+https://github.com/rust-lang/crates.io-index" 362 | checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" 363 | dependencies = [ 364 | "bytes", 365 | "fnv", 366 | "itoa", 367 | ] 368 | 369 | [[package]] 370 | name = "http-body" 371 | version = "1.0.1" 372 | source = "registry+https://github.com/rust-lang/crates.io-index" 373 | checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" 374 | dependencies = [ 375 | "bytes", 376 | "http", 377 | ] 378 | 379 | [[package]] 380 | name = "http-body-util" 381 | version = "0.1.3" 382 | source = "registry+https://github.com/rust-lang/crates.io-index" 383 | checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" 384 | dependencies = [ 385 | "bytes", 386 | "futures-core", 387 | "http", 388 | "http-body", 389 | "pin-project-lite", 390 | ] 391 | 392 | [[package]] 393 | name = "httparse" 394 | version = "1.10.1" 395 | source = "registry+https://github.com/rust-lang/crates.io-index" 396 | checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" 397 | 398 | [[package]] 399 | name = "hyper" 400 | version = "1.7.0" 401 | source = "registry+https://github.com/rust-lang/crates.io-index" 402 | checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" 403 | dependencies = [ 404 | "atomic-waker", 405 | "bytes", 406 | "futures-channel", 407 | "futures-core", 408 | "http", 409 | "http-body", 410 | "httparse", 411 | "itoa", 412 | "pin-project-lite", 413 | "pin-utils", 414 | "smallvec", 415 | "tokio", 416 | "want", 417 | ] 418 | 419 | [[package]] 420 | name = "hyper-rustls" 421 | version = "0.27.7" 422 | source = "registry+https://github.com/rust-lang/crates.io-index" 423 | checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" 424 | dependencies = [ 425 | "http", 426 | "hyper", 427 | "hyper-util", 428 | "rustls", 429 | "rustls-pki-types", 430 | "tokio", 431 | "tokio-rustls", 432 | "tower-service", 433 | "webpki-roots", 434 | ] 435 | 436 | [[package]] 437 | name = "hyper-util" 438 | version = "0.1.17" 439 | source = "registry+https://github.com/rust-lang/crates.io-index" 440 | checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" 441 | dependencies = [ 442 | "base64", 443 | "bytes", 444 | "futures-channel", 445 | "futures-core", 446 | "futures-util", 447 | "http", 448 | "http-body", 449 | "hyper", 450 | "ipnet", 451 | "libc", 452 | "percent-encoding", 453 | "pin-project-lite", 454 | "socket2", 455 | "tokio", 456 | "tower-service", 457 | "tracing", 458 | ] 459 | 460 | [[package]] 461 | name = "idna" 462 | version = "1.1.0" 463 | source = "registry+https://github.com/rust-lang/crates.io-index" 464 | checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" 465 | dependencies = [ 466 | "idna_adapter", 467 | "smallvec", 468 | "utf8_iter", 469 | ] 470 | 471 | [[package]] 472 | name = "idna_adapter" 473 | version = "1.0.0" 474 | source = "registry+https://github.com/rust-lang/crates.io-index" 475 | checksum = "cfdf4f5d937a025381f5ab13624b1c5f51414bfe5c9885663226eae8d6d39560" 476 | 477 | [[package]] 478 | name = "indexmap" 479 | version = "2.3.0" 480 | source = "registry+https://github.com/rust-lang/crates.io-index" 481 | checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0" 482 | dependencies = [ 483 | "equivalent", 484 | "hashbrown", 485 | ] 486 | 487 | [[package]] 488 | name = "ipnet" 489 | version = "2.11.0" 490 | source = "registry+https://github.com/rust-lang/crates.io-index" 491 | checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" 492 | 493 | [[package]] 494 | name = "iri-string" 495 | version = "0.7.9" 496 | source = "registry+https://github.com/rust-lang/crates.io-index" 497 | checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397" 498 | dependencies = [ 499 | "memchr", 500 | "serde", 501 | ] 502 | 503 | [[package]] 504 | name = "is_terminal_polyfill" 505 | version = "1.70.2" 506 | source = "registry+https://github.com/rust-lang/crates.io-index" 507 | checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" 508 | 509 | [[package]] 510 | name = "itoa" 511 | version = "1.0.15" 512 | source = "registry+https://github.com/rust-lang/crates.io-index" 513 | checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 514 | 515 | [[package]] 516 | name = "js-sys" 517 | version = "0.3.82" 518 | source = "registry+https://github.com/rust-lang/crates.io-index" 519 | checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" 520 | dependencies = [ 521 | "once_cell", 522 | "wasm-bindgen", 523 | ] 524 | 525 | [[package]] 526 | name = "libc" 527 | version = "0.2.177" 528 | source = "registry+https://github.com/rust-lang/crates.io-index" 529 | checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" 530 | 531 | [[package]] 532 | name = "log" 533 | version = "0.4.28" 534 | source = "registry+https://github.com/rust-lang/crates.io-index" 535 | checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" 536 | 537 | [[package]] 538 | name = "lru-slab" 539 | version = "0.1.2" 540 | source = "registry+https://github.com/rust-lang/crates.io-index" 541 | checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" 542 | 543 | [[package]] 544 | name = "memchr" 545 | version = "2.7.6" 546 | source = "registry+https://github.com/rust-lang/crates.io-index" 547 | checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" 548 | 549 | [[package]] 550 | name = "mio" 551 | version = "1.1.0" 552 | source = "registry+https://github.com/rust-lang/crates.io-index" 553 | checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" 554 | dependencies = [ 555 | "libc", 556 | "wasi", 557 | "windows-sys 0.61.2", 558 | ] 559 | 560 | [[package]] 561 | name = "num-traits" 562 | version = "0.2.19" 563 | source = "registry+https://github.com/rust-lang/crates.io-index" 564 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 565 | dependencies = [ 566 | "autocfg", 567 | ] 568 | 569 | [[package]] 570 | name = "once_cell" 571 | version = "1.21.3" 572 | source = "registry+https://github.com/rust-lang/crates.io-index" 573 | checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 574 | 575 | [[package]] 576 | name = "once_cell_polyfill" 577 | version = "1.70.2" 578 | source = "registry+https://github.com/rust-lang/crates.io-index" 579 | checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" 580 | 581 | [[package]] 582 | name = "percent-encoding" 583 | version = "2.3.2" 584 | source = "registry+https://github.com/rust-lang/crates.io-index" 585 | checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" 586 | 587 | [[package]] 588 | name = "pin-project-lite" 589 | version = "0.2.16" 590 | source = "registry+https://github.com/rust-lang/crates.io-index" 591 | checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" 592 | 593 | [[package]] 594 | name = "pin-utils" 595 | version = "0.1.0" 596 | source = "registry+https://github.com/rust-lang/crates.io-index" 597 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 598 | 599 | [[package]] 600 | name = "ppv-lite86" 601 | version = "0.2.21" 602 | source = "registry+https://github.com/rust-lang/crates.io-index" 603 | checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" 604 | dependencies = [ 605 | "zerocopy", 606 | ] 607 | 608 | [[package]] 609 | name = "proc-macro2" 610 | version = "1.0.103" 611 | source = "registry+https://github.com/rust-lang/crates.io-index" 612 | checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" 613 | dependencies = [ 614 | "unicode-ident", 615 | ] 616 | 617 | [[package]] 618 | name = "quinn" 619 | version = "0.11.9" 620 | source = "registry+https://github.com/rust-lang/crates.io-index" 621 | checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" 622 | dependencies = [ 623 | "bytes", 624 | "cfg_aliases", 625 | "pin-project-lite", 626 | "quinn-proto", 627 | "quinn-udp", 628 | "rustc-hash", 629 | "rustls", 630 | "socket2", 631 | "thiserror", 632 | "tokio", 633 | "tracing", 634 | "web-time", 635 | ] 636 | 637 | [[package]] 638 | name = "quinn-proto" 639 | version = "0.11.13" 640 | source = "registry+https://github.com/rust-lang/crates.io-index" 641 | checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" 642 | dependencies = [ 643 | "bytes", 644 | "getrandom 0.3.4", 645 | "lru-slab", 646 | "rand", 647 | "ring", 648 | "rustc-hash", 649 | "rustls", 650 | "rustls-pki-types", 651 | "slab", 652 | "thiserror", 653 | "tinyvec", 654 | "tracing", 655 | "web-time", 656 | ] 657 | 658 | [[package]] 659 | name = "quinn-udp" 660 | version = "0.5.14" 661 | source = "registry+https://github.com/rust-lang/crates.io-index" 662 | checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" 663 | dependencies = [ 664 | "cfg_aliases", 665 | "libc", 666 | "once_cell", 667 | "socket2", 668 | "tracing", 669 | "windows-sys 0.60.2", 670 | ] 671 | 672 | [[package]] 673 | name = "quote" 674 | version = "1.0.42" 675 | source = "registry+https://github.com/rust-lang/crates.io-index" 676 | checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" 677 | dependencies = [ 678 | "proc-macro2", 679 | ] 680 | 681 | [[package]] 682 | name = "r-efi" 683 | version = "5.3.0" 684 | source = "registry+https://github.com/rust-lang/crates.io-index" 685 | checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" 686 | 687 | [[package]] 688 | name = "rand" 689 | version = "0.9.2" 690 | source = "registry+https://github.com/rust-lang/crates.io-index" 691 | checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" 692 | dependencies = [ 693 | "rand_chacha", 694 | "rand_core", 695 | ] 696 | 697 | [[package]] 698 | name = "rand_chacha" 699 | version = "0.9.0" 700 | source = "registry+https://github.com/rust-lang/crates.io-index" 701 | checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" 702 | dependencies = [ 703 | "ppv-lite86", 704 | "rand_core", 705 | ] 706 | 707 | [[package]] 708 | name = "rand_core" 709 | version = "0.9.3" 710 | source = "registry+https://github.com/rust-lang/crates.io-index" 711 | checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" 712 | dependencies = [ 713 | "getrandom 0.3.4", 714 | ] 715 | 716 | [[package]] 717 | name = "releases" 718 | version = "0.1.0" 719 | dependencies = [ 720 | "anyhow", 721 | "clap", 722 | "crates_io_api", 723 | "semver", 724 | "serde", 725 | "serde_json", 726 | "tokio", 727 | "toml", 728 | ] 729 | 730 | [[package]] 731 | name = "reqwest" 732 | version = "0.12.24" 733 | source = "registry+https://github.com/rust-lang/crates.io-index" 734 | checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" 735 | dependencies = [ 736 | "base64", 737 | "bytes", 738 | "futures-channel", 739 | "futures-core", 740 | "futures-util", 741 | "http", 742 | "http-body", 743 | "http-body-util", 744 | "hyper", 745 | "hyper-rustls", 746 | "hyper-util", 747 | "js-sys", 748 | "log", 749 | "percent-encoding", 750 | "pin-project-lite", 751 | "quinn", 752 | "rustls", 753 | "rustls-pki-types", 754 | "serde", 755 | "serde_json", 756 | "serde_urlencoded", 757 | "sync_wrapper", 758 | "tokio", 759 | "tokio-rustls", 760 | "tower", 761 | "tower-http", 762 | "tower-service", 763 | "url", 764 | "wasm-bindgen", 765 | "wasm-bindgen-futures", 766 | "web-sys", 767 | "webpki-roots", 768 | ] 769 | 770 | [[package]] 771 | name = "ring" 772 | version = "0.17.14" 773 | source = "registry+https://github.com/rust-lang/crates.io-index" 774 | checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" 775 | dependencies = [ 776 | "cc", 777 | "cfg-if", 778 | "getrandom 0.2.16", 779 | "libc", 780 | "untrusted", 781 | "windows-sys 0.52.0", 782 | ] 783 | 784 | [[package]] 785 | name = "rustc-hash" 786 | version = "2.1.1" 787 | source = "registry+https://github.com/rust-lang/crates.io-index" 788 | checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" 789 | 790 | [[package]] 791 | name = "rustls" 792 | version = "0.23.35" 793 | source = "registry+https://github.com/rust-lang/crates.io-index" 794 | checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" 795 | dependencies = [ 796 | "once_cell", 797 | "ring", 798 | "rustls-pki-types", 799 | "rustls-webpki", 800 | "subtle", 801 | "zeroize", 802 | ] 803 | 804 | [[package]] 805 | name = "rustls-pki-types" 806 | version = "1.13.0" 807 | source = "registry+https://github.com/rust-lang/crates.io-index" 808 | checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a" 809 | dependencies = [ 810 | "web-time", 811 | "zeroize", 812 | ] 813 | 814 | [[package]] 815 | name = "rustls-webpki" 816 | version = "0.103.8" 817 | source = "registry+https://github.com/rust-lang/crates.io-index" 818 | checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" 819 | dependencies = [ 820 | "ring", 821 | "rustls-pki-types", 822 | "untrusted", 823 | ] 824 | 825 | [[package]] 826 | name = "rustversion" 827 | version = "1.0.22" 828 | source = "registry+https://github.com/rust-lang/crates.io-index" 829 | checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" 830 | 831 | [[package]] 832 | name = "ryu" 833 | version = "1.0.20" 834 | source = "registry+https://github.com/rust-lang/crates.io-index" 835 | checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" 836 | 837 | [[package]] 838 | name = "semver" 839 | version = "1.0.27" 840 | source = "registry+https://github.com/rust-lang/crates.io-index" 841 | checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" 842 | 843 | [[package]] 844 | name = "serde" 845 | version = "1.0.228" 846 | source = "registry+https://github.com/rust-lang/crates.io-index" 847 | checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" 848 | dependencies = [ 849 | "serde_core", 850 | "serde_derive", 851 | ] 852 | 853 | [[package]] 854 | name = "serde_core" 855 | version = "1.0.228" 856 | source = "registry+https://github.com/rust-lang/crates.io-index" 857 | checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" 858 | dependencies = [ 859 | "serde_derive", 860 | ] 861 | 862 | [[package]] 863 | name = "serde_derive" 864 | version = "1.0.228" 865 | source = "registry+https://github.com/rust-lang/crates.io-index" 866 | checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" 867 | dependencies = [ 868 | "proc-macro2", 869 | "quote", 870 | "syn", 871 | ] 872 | 873 | [[package]] 874 | name = "serde_json" 875 | version = "1.0.145" 876 | source = "registry+https://github.com/rust-lang/crates.io-index" 877 | checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" 878 | dependencies = [ 879 | "itoa", 880 | "memchr", 881 | "ryu", 882 | "serde", 883 | "serde_core", 884 | ] 885 | 886 | [[package]] 887 | name = "serde_path_to_error" 888 | version = "0.1.20" 889 | source = "registry+https://github.com/rust-lang/crates.io-index" 890 | checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" 891 | dependencies = [ 892 | "itoa", 893 | "serde", 894 | "serde_core", 895 | ] 896 | 897 | [[package]] 898 | name = "serde_spanned" 899 | version = "0.6.9" 900 | source = "registry+https://github.com/rust-lang/crates.io-index" 901 | checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" 902 | dependencies = [ 903 | "serde", 904 | ] 905 | 906 | [[package]] 907 | name = "serde_urlencoded" 908 | version = "0.7.1" 909 | source = "registry+https://github.com/rust-lang/crates.io-index" 910 | checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 911 | dependencies = [ 912 | "form_urlencoded", 913 | "itoa", 914 | "ryu", 915 | "serde", 916 | ] 917 | 918 | [[package]] 919 | name = "shlex" 920 | version = "1.3.0" 921 | source = "registry+https://github.com/rust-lang/crates.io-index" 922 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 923 | 924 | [[package]] 925 | name = "slab" 926 | version = "0.4.11" 927 | source = "registry+https://github.com/rust-lang/crates.io-index" 928 | checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" 929 | 930 | [[package]] 931 | name = "smallvec" 932 | version = "1.15.1" 933 | source = "registry+https://github.com/rust-lang/crates.io-index" 934 | checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" 935 | 936 | [[package]] 937 | name = "socket2" 938 | version = "0.6.1" 939 | source = "registry+https://github.com/rust-lang/crates.io-index" 940 | checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" 941 | dependencies = [ 942 | "libc", 943 | "windows-sys 0.60.2", 944 | ] 945 | 946 | [[package]] 947 | name = "strsim" 948 | version = "0.11.1" 949 | source = "registry+https://github.com/rust-lang/crates.io-index" 950 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 951 | 952 | [[package]] 953 | name = "subtle" 954 | version = "2.6.1" 955 | source = "registry+https://github.com/rust-lang/crates.io-index" 956 | checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 957 | 958 | [[package]] 959 | name = "syn" 960 | version = "2.0.109" 961 | source = "registry+https://github.com/rust-lang/crates.io-index" 962 | checksum = "2f17c7e013e88258aa9543dcbe81aca68a667a9ac37cd69c9fbc07858bfe0e2f" 963 | dependencies = [ 964 | "proc-macro2", 965 | "quote", 966 | "unicode-ident", 967 | ] 968 | 969 | [[package]] 970 | name = "sync_wrapper" 971 | version = "1.0.2" 972 | source = "registry+https://github.com/rust-lang/crates.io-index" 973 | checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" 974 | dependencies = [ 975 | "futures-core", 976 | ] 977 | 978 | [[package]] 979 | name = "thiserror" 980 | version = "2.0.17" 981 | source = "registry+https://github.com/rust-lang/crates.io-index" 982 | checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" 983 | dependencies = [ 984 | "thiserror-impl", 985 | ] 986 | 987 | [[package]] 988 | name = "thiserror-impl" 989 | version = "2.0.17" 990 | source = "registry+https://github.com/rust-lang/crates.io-index" 991 | checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" 992 | dependencies = [ 993 | "proc-macro2", 994 | "quote", 995 | "syn", 996 | ] 997 | 998 | [[package]] 999 | name = "tinyvec" 1000 | version = "1.10.0" 1001 | source = "registry+https://github.com/rust-lang/crates.io-index" 1002 | checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" 1003 | dependencies = [ 1004 | "tinyvec_macros", 1005 | ] 1006 | 1007 | [[package]] 1008 | name = "tinyvec_macros" 1009 | version = "0.1.1" 1010 | source = "registry+https://github.com/rust-lang/crates.io-index" 1011 | checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" 1012 | 1013 | [[package]] 1014 | name = "tokio" 1015 | version = "1.48.0" 1016 | source = "registry+https://github.com/rust-lang/crates.io-index" 1017 | checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" 1018 | dependencies = [ 1019 | "bytes", 1020 | "libc", 1021 | "mio", 1022 | "pin-project-lite", 1023 | "socket2", 1024 | "tokio-macros", 1025 | "windows-sys 0.61.2", 1026 | ] 1027 | 1028 | [[package]] 1029 | name = "tokio-macros" 1030 | version = "2.6.0" 1031 | source = "registry+https://github.com/rust-lang/crates.io-index" 1032 | checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" 1033 | dependencies = [ 1034 | "proc-macro2", 1035 | "quote", 1036 | "syn", 1037 | ] 1038 | 1039 | [[package]] 1040 | name = "tokio-rustls" 1041 | version = "0.26.4" 1042 | source = "registry+https://github.com/rust-lang/crates.io-index" 1043 | checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" 1044 | dependencies = [ 1045 | "rustls", 1046 | "tokio", 1047 | ] 1048 | 1049 | [[package]] 1050 | name = "toml" 1051 | version = "0.8.23" 1052 | source = "registry+https://github.com/rust-lang/crates.io-index" 1053 | checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" 1054 | dependencies = [ 1055 | "serde", 1056 | "serde_spanned", 1057 | "toml_datetime", 1058 | "toml_edit", 1059 | ] 1060 | 1061 | [[package]] 1062 | name = "toml_datetime" 1063 | version = "0.6.11" 1064 | source = "registry+https://github.com/rust-lang/crates.io-index" 1065 | checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" 1066 | dependencies = [ 1067 | "serde", 1068 | ] 1069 | 1070 | [[package]] 1071 | name = "toml_edit" 1072 | version = "0.22.27" 1073 | source = "registry+https://github.com/rust-lang/crates.io-index" 1074 | checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" 1075 | dependencies = [ 1076 | "indexmap", 1077 | "serde", 1078 | "serde_spanned", 1079 | "toml_datetime", 1080 | "toml_write", 1081 | "winnow", 1082 | ] 1083 | 1084 | [[package]] 1085 | name = "toml_write" 1086 | version = "0.1.2" 1087 | source = "registry+https://github.com/rust-lang/crates.io-index" 1088 | checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" 1089 | 1090 | [[package]] 1091 | name = "tower" 1092 | version = "0.5.2" 1093 | source = "registry+https://github.com/rust-lang/crates.io-index" 1094 | checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" 1095 | dependencies = [ 1096 | "futures-core", 1097 | "futures-util", 1098 | "pin-project-lite", 1099 | "sync_wrapper", 1100 | "tokio", 1101 | "tower-layer", 1102 | "tower-service", 1103 | ] 1104 | 1105 | [[package]] 1106 | name = "tower-http" 1107 | version = "0.6.6" 1108 | source = "registry+https://github.com/rust-lang/crates.io-index" 1109 | checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" 1110 | dependencies = [ 1111 | "bitflags", 1112 | "bytes", 1113 | "futures-util", 1114 | "http", 1115 | "http-body", 1116 | "iri-string", 1117 | "pin-project-lite", 1118 | "tower", 1119 | "tower-layer", 1120 | "tower-service", 1121 | ] 1122 | 1123 | [[package]] 1124 | name = "tower-layer" 1125 | version = "0.3.3" 1126 | source = "registry+https://github.com/rust-lang/crates.io-index" 1127 | checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" 1128 | 1129 | [[package]] 1130 | name = "tower-service" 1131 | version = "0.3.3" 1132 | source = "registry+https://github.com/rust-lang/crates.io-index" 1133 | checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" 1134 | 1135 | [[package]] 1136 | name = "tracing" 1137 | version = "0.1.41" 1138 | source = "registry+https://github.com/rust-lang/crates.io-index" 1139 | checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" 1140 | dependencies = [ 1141 | "pin-project-lite", 1142 | "tracing-core", 1143 | ] 1144 | 1145 | [[package]] 1146 | name = "tracing-core" 1147 | version = "0.1.34" 1148 | source = "registry+https://github.com/rust-lang/crates.io-index" 1149 | checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" 1150 | dependencies = [ 1151 | "once_cell", 1152 | ] 1153 | 1154 | [[package]] 1155 | name = "try-lock" 1156 | version = "0.2.5" 1157 | source = "registry+https://github.com/rust-lang/crates.io-index" 1158 | checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" 1159 | 1160 | [[package]] 1161 | name = "unicode-ident" 1162 | version = "1.0.22" 1163 | source = "registry+https://github.com/rust-lang/crates.io-index" 1164 | checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" 1165 | 1166 | [[package]] 1167 | name = "untrusted" 1168 | version = "0.9.0" 1169 | source = "registry+https://github.com/rust-lang/crates.io-index" 1170 | checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" 1171 | 1172 | [[package]] 1173 | name = "url" 1174 | version = "2.5.7" 1175 | source = "registry+https://github.com/rust-lang/crates.io-index" 1176 | checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" 1177 | dependencies = [ 1178 | "form_urlencoded", 1179 | "idna", 1180 | "percent-encoding", 1181 | "serde", 1182 | ] 1183 | 1184 | [[package]] 1185 | name = "utf8_iter" 1186 | version = "1.0.4" 1187 | source = "registry+https://github.com/rust-lang/crates.io-index" 1188 | checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" 1189 | 1190 | [[package]] 1191 | name = "utf8parse" 1192 | version = "0.2.2" 1193 | source = "registry+https://github.com/rust-lang/crates.io-index" 1194 | checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 1195 | 1196 | [[package]] 1197 | name = "want" 1198 | version = "0.3.1" 1199 | source = "registry+https://github.com/rust-lang/crates.io-index" 1200 | checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" 1201 | dependencies = [ 1202 | "try-lock", 1203 | ] 1204 | 1205 | [[package]] 1206 | name = "wasi" 1207 | version = "0.11.1+wasi-snapshot-preview1" 1208 | source = "registry+https://github.com/rust-lang/crates.io-index" 1209 | checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" 1210 | 1211 | [[package]] 1212 | name = "wasip2" 1213 | version = "1.0.1+wasi-0.2.4" 1214 | source = "registry+https://github.com/rust-lang/crates.io-index" 1215 | checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" 1216 | dependencies = [ 1217 | "wit-bindgen", 1218 | ] 1219 | 1220 | [[package]] 1221 | name = "wasm-bindgen" 1222 | version = "0.2.105" 1223 | source = "registry+https://github.com/rust-lang/crates.io-index" 1224 | checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" 1225 | dependencies = [ 1226 | "cfg-if", 1227 | "once_cell", 1228 | "rustversion", 1229 | "wasm-bindgen-macro", 1230 | "wasm-bindgen-shared", 1231 | ] 1232 | 1233 | [[package]] 1234 | name = "wasm-bindgen-futures" 1235 | version = "0.4.55" 1236 | source = "registry+https://github.com/rust-lang/crates.io-index" 1237 | checksum = "551f88106c6d5e7ccc7cd9a16f312dd3b5d36ea8b4954304657d5dfba115d4a0" 1238 | dependencies = [ 1239 | "cfg-if", 1240 | "js-sys", 1241 | "once_cell", 1242 | "wasm-bindgen", 1243 | "web-sys", 1244 | ] 1245 | 1246 | [[package]] 1247 | name = "wasm-bindgen-macro" 1248 | version = "0.2.105" 1249 | source = "registry+https://github.com/rust-lang/crates.io-index" 1250 | checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" 1251 | dependencies = [ 1252 | "quote", 1253 | "wasm-bindgen-macro-support", 1254 | ] 1255 | 1256 | [[package]] 1257 | name = "wasm-bindgen-macro-support" 1258 | version = "0.2.105" 1259 | source = "registry+https://github.com/rust-lang/crates.io-index" 1260 | checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" 1261 | dependencies = [ 1262 | "bumpalo", 1263 | "proc-macro2", 1264 | "quote", 1265 | "syn", 1266 | "wasm-bindgen-shared", 1267 | ] 1268 | 1269 | [[package]] 1270 | name = "wasm-bindgen-shared" 1271 | version = "0.2.105" 1272 | source = "registry+https://github.com/rust-lang/crates.io-index" 1273 | checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" 1274 | dependencies = [ 1275 | "unicode-ident", 1276 | ] 1277 | 1278 | [[package]] 1279 | name = "web-sys" 1280 | version = "0.3.82" 1281 | source = "registry+https://github.com/rust-lang/crates.io-index" 1282 | checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1" 1283 | dependencies = [ 1284 | "js-sys", 1285 | "wasm-bindgen", 1286 | ] 1287 | 1288 | [[package]] 1289 | name = "web-time" 1290 | version = "1.1.0" 1291 | source = "registry+https://github.com/rust-lang/crates.io-index" 1292 | checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" 1293 | dependencies = [ 1294 | "js-sys", 1295 | "wasm-bindgen", 1296 | ] 1297 | 1298 | [[package]] 1299 | name = "webpki-roots" 1300 | version = "1.0.4" 1301 | source = "registry+https://github.com/rust-lang/crates.io-index" 1302 | checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e" 1303 | dependencies = [ 1304 | "rustls-pki-types", 1305 | ] 1306 | 1307 | [[package]] 1308 | name = "windows-link" 1309 | version = "0.2.1" 1310 | source = "registry+https://github.com/rust-lang/crates.io-index" 1311 | checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" 1312 | 1313 | [[package]] 1314 | name = "windows-sys" 1315 | version = "0.52.0" 1316 | source = "registry+https://github.com/rust-lang/crates.io-index" 1317 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 1318 | dependencies = [ 1319 | "windows-targets 0.52.6", 1320 | ] 1321 | 1322 | [[package]] 1323 | name = "windows-sys" 1324 | version = "0.60.2" 1325 | source = "registry+https://github.com/rust-lang/crates.io-index" 1326 | checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" 1327 | dependencies = [ 1328 | "windows-targets 0.53.5", 1329 | ] 1330 | 1331 | [[package]] 1332 | name = "windows-sys" 1333 | version = "0.61.2" 1334 | source = "registry+https://github.com/rust-lang/crates.io-index" 1335 | checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" 1336 | dependencies = [ 1337 | "windows-link", 1338 | ] 1339 | 1340 | [[package]] 1341 | name = "windows-targets" 1342 | version = "0.52.6" 1343 | source = "registry+https://github.com/rust-lang/crates.io-index" 1344 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 1345 | dependencies = [ 1346 | "windows_aarch64_gnullvm 0.52.6", 1347 | "windows_aarch64_msvc 0.52.6", 1348 | "windows_i686_gnu 0.52.6", 1349 | "windows_i686_gnullvm 0.52.6", 1350 | "windows_i686_msvc 0.52.6", 1351 | "windows_x86_64_gnu 0.52.6", 1352 | "windows_x86_64_gnullvm 0.52.6", 1353 | "windows_x86_64_msvc 0.52.6", 1354 | ] 1355 | 1356 | [[package]] 1357 | name = "windows-targets" 1358 | version = "0.53.5" 1359 | source = "registry+https://github.com/rust-lang/crates.io-index" 1360 | checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" 1361 | dependencies = [ 1362 | "windows-link", 1363 | "windows_aarch64_gnullvm 0.53.1", 1364 | "windows_aarch64_msvc 0.53.1", 1365 | "windows_i686_gnu 0.53.1", 1366 | "windows_i686_gnullvm 0.53.1", 1367 | "windows_i686_msvc 0.53.1", 1368 | "windows_x86_64_gnu 0.53.1", 1369 | "windows_x86_64_gnullvm 0.53.1", 1370 | "windows_x86_64_msvc 0.53.1", 1371 | ] 1372 | 1373 | [[package]] 1374 | name = "windows_aarch64_gnullvm" 1375 | version = "0.52.6" 1376 | source = "registry+https://github.com/rust-lang/crates.io-index" 1377 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 1378 | 1379 | [[package]] 1380 | name = "windows_aarch64_gnullvm" 1381 | version = "0.53.1" 1382 | source = "registry+https://github.com/rust-lang/crates.io-index" 1383 | checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" 1384 | 1385 | [[package]] 1386 | name = "windows_aarch64_msvc" 1387 | version = "0.52.6" 1388 | source = "registry+https://github.com/rust-lang/crates.io-index" 1389 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 1390 | 1391 | [[package]] 1392 | name = "windows_aarch64_msvc" 1393 | version = "0.53.1" 1394 | source = "registry+https://github.com/rust-lang/crates.io-index" 1395 | checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" 1396 | 1397 | [[package]] 1398 | name = "windows_i686_gnu" 1399 | version = "0.52.6" 1400 | source = "registry+https://github.com/rust-lang/crates.io-index" 1401 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 1402 | 1403 | [[package]] 1404 | name = "windows_i686_gnu" 1405 | version = "0.53.1" 1406 | source = "registry+https://github.com/rust-lang/crates.io-index" 1407 | checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" 1408 | 1409 | [[package]] 1410 | name = "windows_i686_gnullvm" 1411 | version = "0.52.6" 1412 | source = "registry+https://github.com/rust-lang/crates.io-index" 1413 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 1414 | 1415 | [[package]] 1416 | name = "windows_i686_gnullvm" 1417 | version = "0.53.1" 1418 | source = "registry+https://github.com/rust-lang/crates.io-index" 1419 | checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" 1420 | 1421 | [[package]] 1422 | name = "windows_i686_msvc" 1423 | version = "0.52.6" 1424 | source = "registry+https://github.com/rust-lang/crates.io-index" 1425 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 1426 | 1427 | [[package]] 1428 | name = "windows_i686_msvc" 1429 | version = "0.53.1" 1430 | source = "registry+https://github.com/rust-lang/crates.io-index" 1431 | checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" 1432 | 1433 | [[package]] 1434 | name = "windows_x86_64_gnu" 1435 | version = "0.52.6" 1436 | source = "registry+https://github.com/rust-lang/crates.io-index" 1437 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 1438 | 1439 | [[package]] 1440 | name = "windows_x86_64_gnu" 1441 | version = "0.53.1" 1442 | source = "registry+https://github.com/rust-lang/crates.io-index" 1443 | checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" 1444 | 1445 | [[package]] 1446 | name = "windows_x86_64_gnullvm" 1447 | version = "0.52.6" 1448 | source = "registry+https://github.com/rust-lang/crates.io-index" 1449 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 1450 | 1451 | [[package]] 1452 | name = "windows_x86_64_gnullvm" 1453 | version = "0.53.1" 1454 | source = "registry+https://github.com/rust-lang/crates.io-index" 1455 | checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" 1456 | 1457 | [[package]] 1458 | name = "windows_x86_64_msvc" 1459 | version = "0.52.6" 1460 | source = "registry+https://github.com/rust-lang/crates.io-index" 1461 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 1462 | 1463 | [[package]] 1464 | name = "windows_x86_64_msvc" 1465 | version = "0.53.1" 1466 | source = "registry+https://github.com/rust-lang/crates.io-index" 1467 | checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" 1468 | 1469 | [[package]] 1470 | name = "winnow" 1471 | version = "0.7.13" 1472 | source = "registry+https://github.com/rust-lang/crates.io-index" 1473 | checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" 1474 | dependencies = [ 1475 | "memchr", 1476 | ] 1477 | 1478 | [[package]] 1479 | name = "wit-bindgen" 1480 | version = "0.46.0" 1481 | source = "registry+https://github.com/rust-lang/crates.io-index" 1482 | checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" 1483 | 1484 | [[package]] 1485 | name = "xshell" 1486 | version = "0.2.7" 1487 | source = "registry+https://github.com/rust-lang/crates.io-index" 1488 | checksum = "9e7290c623014758632efe00737145b6867b66292c42167f2ec381eb566a373d" 1489 | dependencies = [ 1490 | "xshell-macros", 1491 | ] 1492 | 1493 | [[package]] 1494 | name = "xshell-macros" 1495 | version = "0.2.7" 1496 | source = "registry+https://github.com/rust-lang/crates.io-index" 1497 | checksum = "32ac00cd3f8ec9c1d33fb3e7958a82df6989c42d747bd326c822b1d625283547" 1498 | 1499 | [[package]] 1500 | name = "zerocopy" 1501 | version = "0.8.27" 1502 | source = "registry+https://github.com/rust-lang/crates.io-index" 1503 | checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" 1504 | dependencies = [ 1505 | "zerocopy-derive", 1506 | ] 1507 | 1508 | [[package]] 1509 | name = "zerocopy-derive" 1510 | version = "0.8.27" 1511 | source = "registry+https://github.com/rust-lang/crates.io-index" 1512 | checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" 1513 | dependencies = [ 1514 | "proc-macro2", 1515 | "quote", 1516 | "syn", 1517 | ] 1518 | 1519 | [[package]] 1520 | name = "zeroize" 1521 | version = "1.8.2" 1522 | source = "registry+https://github.com/rust-lang/crates.io-index" 1523 | checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" 1524 | --------------------------------------------------------------------------------