# Check ingredients of published Rust crates

This crate implements two modes for checking ingredients of Rust crates:

* `report`: Comparing published crate sources from [crates.io] with the associated contents of
  the project's version control system.
* `diff`: Compare contents of two different, published versions of the same crate.

## `report` mode

This comparison mode reads crate metadata (from `Cargo.toml` and from the `.cargo_vcs_info.json`
file that is embedded by cargo during the publishing process), fetches the corresponding `git`
repository, and compares the contents of the published crate with the contents of the project
repository at the `ref` that is recorded in `.cargo_vcs_info.json`.

Any actual differences in file contents or crate metadata are considered to be *errors*. Issues
that prevent checking for differences (like missing metadata or an invalid git repository) are
considered *fatal*.

Example using the Rust API:

```rust,no_run
use ingredients::Crate;

#[tokio::main]
async fn main() {
    let krate = Crate::download("syn", "2.0.111").await.unwrap();
    let report = krate.report().await.unwrap();
    println!("{report}");
}
```

Example using the CLI:

```sh
ingredients report syn 2.0.111
```

Some differences are "expected" and are not reported:

* The `Cargo.toml` file is processed and rewritten during the publishing process. Instead, the
  `Cargo.toml` contents from the repository is compared to `Cargo.toml.orig`, which contains the
  original, unmodified file contents.
* Instead, crate metadata is compared by parsing `Cargo.toml` contents and checking for
  semantic equivalence instead of byte-for-byte equivalence.
* Dependencies that are "path"-based are stripped by cargo during the publishing process.
  Differences in crate dependencies that are solely due to "path"-based dependencies having been
  stripped are ignored and not reported.
* Symbolic links present in the project repository are resolved during the publishing process,
  cargo includes actual files in published crates instead.

## `diff` mode

This mode compares crate metadata and contents between two source archives that were published
to [crates.io]. It is much less strict than the "report" mode (because differences are expected
when comparing two different versions of a crate), but will report differences with more
granularity than just reporting an error on *any* difference. As such, the only difference that
actually triggers a lint with "error" severity is if the crate *name* is different. This should
only ever happen when comparing two *different* crates (but which might be useful to do in cases
where a crate is "renamed", i.e. new versions are published under a different name).

Example using the Rust API:

```rust,no_run
use ingredients::Crate;

#[tokio::main]
async fn main() {
    let old_krate = Crate::download("syn", "2.0.110").await.unwrap();
    let new_krate = Crate::download("syn", "2.0.111").await.unwrap();
    let diff = old_krate.diff(&new_krate).unwrap();
    println!("{diff}");
}
```

Example using the CLI:

```sh
ingredients diff syn 2.0.110 2.0.111
```

## Features

* `cli` (disabled by default)

Almost all functionality from the crate API is also available from the command-line interface.
To build the `ingredients` command-line program, compile with the `cli` feature enabled.

## Installation

From [crates.io]: `cargo install -F cli ingredients`

## External dependencies

**Building**:

* `cargo` (refer to `package.rust-version` in `Cargo.toml` for the minimum supported Rust version)
* `openssl` development headers must be available (for `reqwest/native-tls`)

**Runtime**:

* `cargo` must be available in `$PATH`

Loading and parsing crate metadata is implemented based on the `cargo_metadata` crate, which calls
`cargo metadata` internally.

* `git` must be available in `$PATH` for "report" mode

The `git` command is used for checking out git repositories in `Crate::report`, and in turn, it
is also required by the `ingredients report` subcommand.

Moving to a solution for cloning / checkout out git repositories that does not rely on an external
`git` command is planned, but currently blocked by missing support for shallow clones / cloning
non-branch refs in `gitoxide` (see <https://github.com/GitoxideLabs/gitoxide/discussions/2309>),
among other issues.

[crates.io]: https://crates.io
