cargo-msrv 0.14.0

Find your minimum supported Rust version (MSRV)!
Documentation
use crate::check::Check;
use crate::outcome::{FailureOutcome, Outcome, SuccessOutcome};
use crate::reporter::{write_failed_check, write_succeeded_check};
use crate::search_methods::FindMinimalCapableToolchain;
use crate::toolchain::{OwnedToolchainSpec, ToolchainSpec};
use crate::{Config, MinimalCompatibility, Output, ProgressAction, TResult};
use bisector::{Bisector, ConvergeTo, Indices, Step};
use rust_releases::Release;

pub struct Bisect<R: Check> {
    runner: R,
}

impl<R: Check> Bisect<R> {
    pub fn new(runner: R) -> Self {
        Self { runner }
    }

    fn run_check(
        runner: &R,
        release: &Release,
        config: &Config,
        output: &impl Output,
    ) -> TResult<ConvergeTo<FailureOutcome, SuccessOutcome>> {
        output.progress(ProgressAction::Checking(release.version()));

        let toolchain = ToolchainSpec::new(release.version(), config.target());
        match runner.check(config, &toolchain) {
            Ok(outcome) => match outcome {
                Outcome::Success(outcome) => Ok(ConvergeTo::Right(outcome)),
                Outcome::Failure(outcome) => Ok(ConvergeTo::Left(outcome)),
            },
            Err(err) => Err(err),
        }
    }

    fn update_progress_bar(iteration: u64, indices: Indices, output: &impl Output) {
        let remainder = (indices.right - indices.left) as u64;
        output.set_steps(remainder + iteration);
    }

    fn minimum_capable(
        releases: &[Release],
        index_of_msrv: Option<Indices>,
        config: &Config,
    ) -> MinimalCompatibility {
        index_of_msrv.map_or(MinimalCompatibility::NoCompatibleToolchains, |i| {
            let version = releases[i.middle()].version();

            MinimalCompatibility::CapableToolchain {
                toolchain: OwnedToolchainSpec::new(version, config.target()),
            }
        })
    }
}

impl<R: Check> FindMinimalCapableToolchain for Bisect<R> {
    fn find_toolchain(
        &self,
        search_space: &[Release],
        config: &Config,
        output: &impl Output,
    ) -> TResult<MinimalCompatibility> {
        let searcher = Bisector::new(search_space);

        let mut iteration = 0_u64;
        let mut indices = Indices::from_bisector(&searcher);

        let mut last_compatible_index = None;

        while let Step {
            indices: next_indices,
            result: Some(step),
        } = searcher.try_bisect(
            |release| Self::run_check(&self.runner, release, config, output),
            indices,
        )? {
            iteration += 1;

            Self::update_progress_bar(iteration, next_indices, output);

            match step {
                ConvergeTo::Left(outcome) => {
                    write_failed_check(&outcome, config, output);
                }
                ConvergeTo::Right(outcome) => {
                    last_compatible_index = Some(indices);
                    write_succeeded_check(&outcome, config, output)
                }
            }

            indices = next_indices;
        }

        Ok(Self::minimum_capable(
            search_space,
            last_compatible_index,
            config,
        ))
    }
}