internal-tools/fixture-manager/src/hakari_toml.rs (115 lines of code) (raw):
// Copyright (c) The cargo-guppy Contributors
// SPDX-License-Identifier: MIT OR Apache-2.0
use crate::context::ContextImpl;
use anyhow::Result;
use camino::{Utf8Path, Utf8PathBuf};
use fixtures::json::*;
use hakari::{diffy::PatchFormatter, Hakari, HakariBuilder, HakariCargoToml, HakariOutputOptions};
use once_cell::sync::Lazy;
use proptest::prelude::*;
use proptest_ext::ValueGenerator;
pub struct HakariTomlContext;
impl<'g> ContextImpl<'g> for HakariTomlContext {
type IterArgs = usize;
type IterItem = (usize, HakariTomlItem<'g>);
type Existing = HakariCargoToml;
fn dir_name(fixture: &'g JsonFixture) -> Utf8PathBuf {
fixture
.abs_path()
.parent()
.expect("up to dirname of summary")
.join("hakari")
}
fn file_name(fixture: &'g JsonFixture, &(count, _): &Self::IterItem) -> String {
format!("{}-{}.toml", fixture.name(), count)
}
fn iter(
fixture: &'g JsonFixture,
&count: &Self::IterArgs,
) -> Box<dyn Iterator<Item = Self::IterItem> + 'g> {
// Make a fresh generator for each output so that filtering by --fixtures continues to
// produce deterministic results.
let mut generator = ValueGenerator::from_seed(fixture.name());
let graph = fixture.graph();
// TODO: add tests for hakari id -- none of our fixtures appear to have a
// workspace-hack or other Hakari package
let hakari_builder_strategy = HakariBuilder::prop010_strategy(graph, Just(None));
let iter = (0..count).map(move |idx| {
// The partial clones mean that a change to the algorithm in part of the strategy won't
// affect the rest of it.
let mut iter_generator = generator.partial_clone();
let mut builder = iter_generator
.partial_clone()
.generate(&hakari_builder_strategy);
// The alternate fixture uses this registry.
if fixture.name() == "metadata_alternate_registries" {
builder.add_registries([("my-registry", METADATA_ALTERNATE_REGISTRY_URL)]);
}
let hakari = builder.compute();
let mut output_options = HakariOutputOptions::default();
output_options
.set_builder_summary(true)
.set_absolute_paths(true);
let toml = hakari
.to_toml_string(&output_options)
.expect("to_toml_string worked");
(idx, HakariTomlItem { hakari, toml })
});
Box::new(iter)
}
fn parse_existing(path: &Utf8Path, contents: String) -> Result<Self::Existing> {
Ok(HakariCargoToml::new_in_memory(path, contents)?)
}
fn is_changed((_, item): &Self::IterItem, existing: &Self::Existing) -> bool {
existing.is_changed(&item.toml)
}
fn diff(
_fixture: &'g JsonFixture,
(_, item): &Self::IterItem,
existing: Option<&Self::Existing>,
) -> String {
static DEFAULT_EXISTING: Lazy<HakariCargoToml> = Lazy::new(|| {
let contents = format!(
"{}{}",
HakariCargoToml::BEGIN_SECTION,
HakariCargoToml::END_SECTION
);
HakariCargoToml::new_in_memory("default", contents)
.expect("contents are in correct format")
});
let existing = existing.unwrap_or(&*DEFAULT_EXISTING);
let diff = existing.diff_toml(&item.toml);
let formatter = PatchFormatter::new();
format!("{}", formatter.fmt_patch(&diff))
// let package_id = guppy::PackageId::new(
// "curl-sys 0.4.36+curl-7.71.1 (registry+https://github.com/rust-lang/crates.io-index)",
// );
// let explain = item.hakari.explain(&package_id);
// let explain = if let Ok(explain) = explain {
// format!("{}", explain.display())
// } else {
// "".to_owned()
// };
// format!("{}\n\n{}", formatter.fmt_patch(&diff), explain)
}
fn write_to_string(
fixture: &'g JsonFixture,
(_, item): &Self::IterItem,
out: &mut String,
) -> Result<()> {
// XXX this should be unified with `DEFAULT_EXISTING` somehow, bleh
let out_contents = format!(
"# This file is @generated. To regenerate, run:\n\
# cargo run -p fixture-manager -- generate-hakari --fixture {}\n\
\n\
### BEGIN HAKARI SECTION\n\
\n\
### END HAKARI SECTION\n\
\n\
# This part of the file should be preserved at the end.\n",
fixture.name()
);
let new_toml = HakariCargoToml::new_in_memory("bogus", out_contents)?;
Ok(new_toml.write_to_fmt(&item.toml, out)?)
}
}
pub struct HakariTomlItem<'g> {
#[allow(dead_code)]
hakari: Hakari<'g>,
toml: String,
}