|
@@ -6,7 +6,7 @@ use std::{
|
|
|
};
|
|
|
|
|
|
use anyhow::{bail, Context as _, Result};
|
|
|
-use cargo_metadata::{Metadata, Package};
|
|
|
+use cargo_metadata::{Metadata, Package, Target};
|
|
|
use clap::Parser;
|
|
|
use dialoguer::{theme::ColorfulTheme, Confirm};
|
|
|
use diff::{lines, Result as Diff};
|
|
@@ -17,11 +17,15 @@ pub struct Options {
|
|
|
/// Bless new API changes.
|
|
|
#[clap(long)]
|
|
|
pub bless: bool,
|
|
|
+
|
|
|
+ /// Bless new API changes.
|
|
|
+ #[clap(long)]
|
|
|
+ pub target: Option<String>,
|
|
|
}
|
|
|
|
|
|
pub fn public_api(options: Options, metadata: Metadata) -> Result<()> {
|
|
|
let toolchain = "nightly";
|
|
|
- let Options { bless } = options;
|
|
|
+ let Options { bless, target } = options;
|
|
|
|
|
|
if !rustup_toolchain::is_installed(toolchain)? {
|
|
|
if Confirm::with_theme(&ColorfulTheme::default())
|
|
@@ -42,21 +46,40 @@ pub fn public_api(options: Options, metadata: Metadata) -> Result<()> {
|
|
|
|
|
|
let errors: Vec<_> = packages
|
|
|
.into_iter()
|
|
|
- .map(|Package { name, publish, .. }| {
|
|
|
- if matches!(publish, Some(publish) if publish.is_empty()) {
|
|
|
- Ok(())
|
|
|
- } else {
|
|
|
- let diff = check_package_api(&name, toolchain, bless, workspace_root.as_std_path())
|
|
|
- .with_context(|| format!("{name} failed to check public API"))?;
|
|
|
- if diff.is_empty() {
|
|
|
+ .map(
|
|
|
+ |Package {
|
|
|
+ name,
|
|
|
+ publish,
|
|
|
+ targets,
|
|
|
+ ..
|
|
|
+ }| {
|
|
|
+ if matches!(publish, Some(publish) if publish.is_empty()) {
|
|
|
Ok(())
|
|
|
} else {
|
|
|
- Err(anyhow::anyhow!(
|
|
|
- "{name} public API changed; re-run with --bless. diff:\n{diff}"
|
|
|
- ))
|
|
|
+ let target = target.as_ref().and_then(|target| {
|
|
|
+ let proc_macro = targets.iter().any(|Target { kind, .. }| {
|
|
|
+ kind.iter().any(|kind| kind == "proc-macro")
|
|
|
+ });
|
|
|
+ (!proc_macro).then_some(target)
|
|
|
+ });
|
|
|
+ let diff = check_package_api(
|
|
|
+ &name,
|
|
|
+ toolchain,
|
|
|
+ target.cloned(),
|
|
|
+ bless,
|
|
|
+ workspace_root.as_std_path(),
|
|
|
+ )
|
|
|
+ .with_context(|| format!("{name} failed to check public API"))?;
|
|
|
+ if diff.is_empty() {
|
|
|
+ Ok(())
|
|
|
+ } else {
|
|
|
+ Err(anyhow::anyhow!(
|
|
|
+ "{name} public API changed; re-run with --bless. diff:\n{diff}"
|
|
|
+ ))
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
- })
|
|
|
+ },
|
|
|
+ )
|
|
|
.filter_map(|result| match result {
|
|
|
Ok(()) => None,
|
|
|
Err(err) => Some(err),
|
|
@@ -73,6 +96,7 @@ pub fn public_api(options: Options, metadata: Metadata) -> Result<()> {
|
|
|
fn check_package_api(
|
|
|
package: &str,
|
|
|
toolchain: &str,
|
|
|
+ target: Option<String>,
|
|
|
bless: bool,
|
|
|
workspace_root: &Path,
|
|
|
) -> Result<String> {
|
|
@@ -82,16 +106,28 @@ fn check_package_api(
|
|
|
.join(package)
|
|
|
.with_extension("txt");
|
|
|
|
|
|
- let rustdoc_json = rustdoc_json::Builder::default()
|
|
|
+ let mut builder = rustdoc_json::Builder::default()
|
|
|
.toolchain(toolchain)
|
|
|
.package(package)
|
|
|
- .all_features(true)
|
|
|
- .build()
|
|
|
- .context("rustdoc_json::Builder::build")?;
|
|
|
+ .all_features(true);
|
|
|
+ if let Some(target) = target {
|
|
|
+ builder = builder.target(target);
|
|
|
+ }
|
|
|
+ let rustdoc_json = builder.build().with_context(|| {
|
|
|
+ format!(
|
|
|
+ "rustdoc_json::Builder::default().toolchain({}).package({}).build()",
|
|
|
+ toolchain, package
|
|
|
+ )
|
|
|
+ })?;
|
|
|
|
|
|
- let public_api = public_api::Builder::from_rustdoc_json(rustdoc_json)
|
|
|
+ let public_api = public_api::Builder::from_rustdoc_json(&rustdoc_json)
|
|
|
.build()
|
|
|
- .context("public_api::Builder::build")?;
|
|
|
+ .with_context(|| {
|
|
|
+ format!(
|
|
|
+ "public_api::Builder::from_rustdoc_json({})::build()",
|
|
|
+ rustdoc_json.display()
|
|
|
+ )
|
|
|
+ })?;
|
|
|
|
|
|
if bless {
|
|
|
let mut output =
|