cargo_msrv/sub_command/
verify.rs1use camino::Utf8PathBuf;
2use cargo_metadata::MetadataCommand;
3use std::convert::TryFrom;
4
5use rust_releases::{Release, ReleaseIndex};
6
7use crate::compatibility::IsCompatible;
8use crate::context::{EnvironmentContext, VerifyContext};
9use crate::error::{CargoMSRVError, TResult};
10use crate::manifest::CargoManifest;
11use crate::manifest::bare_version::BareVersion;
12use crate::outcome::Compatibility;
13use crate::reporter::Reporter;
14use crate::reporter::event::VerifyResult;
15use crate::rust::Toolchain;
16use crate::sub_command::SubCommand;
17
18pub struct Verify<'index, C: IsCompatible> {
20 release_index: &'index ReleaseIndex,
21 runner: C,
22}
23
24impl<'index, C: IsCompatible> Verify<'index, C> {
25 pub fn new(release_index: &'index ReleaseIndex, runner: C) -> Self {
29 Self {
30 release_index,
31 runner,
32 }
33 }
34}
35
36impl<C: IsCompatible> SubCommand for Verify<'_, C> {
37 type Context = VerifyContext;
38 type Output = ();
39
40 fn run(&self, ctx: &Self::Context, reporter: &impl Reporter) -> TResult<Self::Output> {
42 let rust_version = ctx.rust_version.clone();
44
45 verify_msrv(
46 reporter,
47 ctx,
48 self.release_index,
49 rust_version,
50 &self.runner,
51 )?;
52
53 Ok(())
54 }
55}
56
57fn verify_msrv(
60 reporter: &impl Reporter,
61 ctx: &VerifyContext,
62 release_index: &ReleaseIndex,
63 rust_version: RustVersion,
64 runner: &impl IsCompatible,
65) -> TResult<()> {
66 let bare_version = rust_version.version();
67 let version =
68 bare_version.try_to_semver(release_index.releases().iter().map(Release::version))?;
69
70 let target = ctx.toolchain.target;
71 let components = ctx.toolchain.components;
72 let toolchain = Toolchain::new(version.clone(), target, components);
73
74 match runner.is_compatible(&toolchain)? {
75 Compatibility::Compatible(_) => success(reporter, toolchain),
76 Compatibility::Incompatible(f) => {
77 failure(reporter, toolchain, rust_version, Some(f.error_message))
78 }
79 }
80}
81
82fn success(reporter: &impl Reporter, toolchain: Toolchain) -> TResult<()> {
84 reporter.report_event(VerifyResult::compatible(toolchain))?;
85 Ok(())
86}
87
88fn failure(
90 reporter: &impl Reporter,
91 toolchain: Toolchain,
92 rust_version: RustVersion,
93 error: Option<String>,
94) -> TResult<()> {
95 reporter.report_event(VerifyResult::incompatible(toolchain, error))?;
96
97 Err(CargoMSRVError::SubCommandVerify(Error::VerifyFailed(
98 VerifyFailed::from(rust_version),
99 )))
100}
101
102#[derive(Debug, thiserror::Error)]
104pub enum Error {
105 #[error(
106 "Crate source was found to be incompatible with Rust version '{}' specified {}", .0.rust_version, .0.source
107 )]
108 VerifyFailed(VerifyFailed),
109}
110
111#[derive(Debug)]
117pub struct VerifyFailed {
118 rust_version: BareVersion,
119 source: RustVersionSource,
120}
121
122impl From<RustVersion> for VerifyFailed {
123 fn from(value: RustVersion) -> Self {
124 VerifyFailed {
125 rust_version: value.rust_version,
126 source: value.source,
127 }
128 }
129}
130
131#[derive(Clone, Debug)]
134pub struct RustVersion {
135 rust_version: BareVersion,
136 source: RustVersionSource,
137}
138
139impl RustVersion {
140 pub fn from_arg(rust_version: BareVersion) -> Self {
141 Self {
142 rust_version,
143 source: RustVersionSource::Arg,
144 }
145 }
146
147 pub fn try_from_environment(env: &EnvironmentContext) -> TResult<Self> {
148 let manifest_path = env.manifest();
149
150 let metadata = MetadataCommand::new()
151 .manifest_path(&manifest_path)
152 .exec()?;
153 CargoManifest::try_from(metadata)?
154 .minimum_rust_version()
155 .ok_or_else(|| CargoMSRVError::NoMSRVKeyInCargoToml(manifest_path.clone()))
156 .map(|v| RustVersion {
157 rust_version: v.clone(),
158 source: RustVersionSource::Manifest(manifest_path.clone()),
159 })
160 }
161
162 pub fn version(&self) -> &BareVersion {
164 &self.rust_version
165 }
166
167 pub fn into_version(self) -> BareVersion {
169 self.rust_version
170 }
171}
172
173#[derive(Clone, Debug, thiserror::Error)]
175enum RustVersionSource {
176 #[error("as --rust-version argument")]
177 Arg,
178
179 #[error("as MSRV in the Cargo manifest located at '{0}'")]
180 Manifest(Utf8PathBuf),
181}