#![doc = include_str!("../examples/list_public_api.rs")]
#![doc = include_str!("../examples/diff_public_api.rs")]
#![warn(missing_docs)]
mod crate_wrapper;
mod error;
mod intermediate_public_item;
mod item_processor;
mod nameable_item;
mod path_component;
mod public_item;
mod render;
pub mod tokens;
pub mod diff;
use std::path::PathBuf;
pub use error::{Error, Result};
pub use public_item::PublicItem;
pub const MINIMUM_NIGHTLY_RUST_VERSION: &str = "nightly-2025-08-02";
#[derive(Copy, Clone, Debug)]
struct BuilderOptions {
sorted: bool,
debug_sorting: bool,
omit_blanket_impls: bool,
omit_auto_trait_impls: bool,
omit_auto_derived_impls: bool,
}
#[derive(Debug, Clone)]
pub struct Builder {
rustdoc_json: PathBuf,
options: BuilderOptions,
}
impl Builder {
#[must_use]
pub fn from_rustdoc_json(path: impl Into<PathBuf>) -> Self {
let options = BuilderOptions {
sorted: true,
debug_sorting: false,
omit_blanket_impls: false,
omit_auto_trait_impls: false,
omit_auto_derived_impls: false,
};
Self {
rustdoc_json: path.into(),
options,
}
}
#[must_use]
pub fn sorted(mut self, sorted: bool) -> Self {
self.options.sorted = sorted;
self
}
#[must_use]
pub fn debug_sorting(mut self, debug_sorting: bool) -> Self {
self.options.debug_sorting = debug_sorting;
self
}
#[must_use]
pub fn omit_blanket_impls(mut self, omit_blanket_impls: bool) -> Self {
self.options.omit_blanket_impls = omit_blanket_impls;
self
}
#[must_use]
pub fn omit_auto_trait_impls(mut self, omit_auto_trait_impls: bool) -> Self {
self.options.omit_auto_trait_impls = omit_auto_trait_impls;
self
}
#[must_use]
pub fn omit_auto_derived_impls(mut self, omit_auto_derived_impls: bool) -> Self {
self.options.omit_auto_derived_impls = omit_auto_derived_impls;
self
}
pub fn build(self) -> Result<PublicApi> {
from_rustdoc_json_str(std::fs::read_to_string(self.rustdoc_json)?, self.options)
}
}
#[derive(Debug)]
#[non_exhaustive] pub struct PublicApi {
pub(crate) items: Vec<PublicItem>,
pub(crate) missing_item_ids: Vec<u32>,
}
impl PublicApi {
pub fn items(&self) -> impl Iterator<Item = &'_ PublicItem> {
self.items.iter()
}
pub fn into_items(self) -> impl Iterator<Item = PublicItem> {
self.items.into_iter()
}
pub fn missing_item_ids(&self) -> impl Iterator<Item = &u32> {
self.missing_item_ids.iter()
}
#[cfg(feature = "snapshot-testing")]
#[track_caller]
pub fn assert_eq_or_update(&self, snapshot_path: impl AsRef<std::path::Path>) {
snapshot_testing::assert_eq_or_update(self.to_string(), snapshot_path);
}
}
impl std::fmt::Display for PublicApi {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for item in self.items() {
writeln!(f, "{item}")?;
}
Ok(())
}
}
fn from_rustdoc_json_str(
rustdoc_json_str: impl AsRef<str>,
options: BuilderOptions,
) -> Result<PublicApi> {
let crate_ = deserialize_without_recursion_limit(rustdoc_json_str.as_ref())?;
let mut public_api = item_processor::public_api_in_crate(&crate_, options);
if options.sorted {
public_api.items.sort_by(PublicItem::grouping_cmp);
}
Ok(public_api)
}
fn deserialize_without_recursion_limit(rustdoc_json_str: &str) -> Result<rustdoc_types::Crate> {
let mut deserializer = serde_json::Deserializer::from_str(rustdoc_json_str);
deserializer.disable_recursion_limit();
Ok(serde::de::Deserialize::deserialize(&mut deserializer)?)
}