fixture_manager/
summaries.rs

1// Copyright (c) The cargo-guppy Contributors
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4use crate::context::ContextImpl;
5use anyhow::Result;
6use camino::{Utf8Path, Utf8PathBuf};
7use fixtures::json::JsonFixture;
8use guppy::graph::{
9    cargo::CargoSet,
10    summaries::{diff::SummaryDiff, Summary},
11};
12use guppy_cmdlib::PackagesAndFeatures;
13use once_cell::sync::Lazy;
14use proptest_ext::ValueGenerator;
15use std::fmt::Write;
16
17pub struct SummaryContext;
18
19impl<'g> ContextImpl<'g> for SummaryContext {
20    type IterArgs = usize;
21    type IterItem = (usize, Summary);
22    type Existing = Summary;
23
24    fn dir_name(fixture: &'g JsonFixture) -> Utf8PathBuf {
25        fixture
26            .abs_path()
27            .parent()
28            .expect("up to dirname of summary")
29            .join("summaries")
30    }
31
32    fn file_name(fixture: &'g JsonFixture, &(count, _): &Self::IterItem) -> String {
33        format!("{}-{}.toml", fixture.name(), count)
34    }
35
36    fn iter(
37        fixture: &'g JsonFixture,
38        &count: &Self::IterArgs,
39    ) -> Box<dyn Iterator<Item = Self::IterItem> + 'g> {
40        // Make a fresh generator for each summary so that filtering by --fixtures continues to
41        // produce deterministic results.
42        let mut generator = ValueGenerator::from_seed(fixture.name());
43
44        let graph = fixture.graph();
45
46        let packages_features_strategy = PackagesAndFeatures::strategy(graph);
47        let cargo_opts_strategy = graph.proptest1_cargo_options_strategy();
48
49        let iter = (0..count).map(move |idx| {
50            // The partial clones mean that e.g. a change to the algorithm in
51            // packages_features_strategy won't affect generation of cargo_opts.
52            let mut iter_generator = generator.partial_clone();
53
54            let packages_features = iter_generator
55                .partial_clone()
56                .generate(&packages_features_strategy);
57            let (initials, features_only) = packages_features
58                .make_feature_sets(graph)
59                .expect("valid feature set");
60
61            let cargo_opts = iter_generator
62                .partial_clone()
63                .generate(&cargo_opts_strategy);
64            let cargo_set = CargoSet::new(initials, features_only, &cargo_opts)
65                .expect("into_cargo_set succeeded");
66
67            (
68                idx,
69                cargo_set
70                    .to_summary(&cargo_opts)
71                    .expect("generated summaries should serialize correctly"),
72            )
73        });
74
75        Box::new(iter)
76    }
77
78    fn parse_existing(_: &Utf8Path, contents: String) -> Result<Self::Existing> {
79        Ok(Summary::parse(&contents)?)
80    }
81
82    fn is_changed((_, summary): &Self::IterItem, existing: &Self::Existing) -> bool {
83        let diff = SummaryDiff::new(existing, summary);
84        diff.is_changed() || existing.metadata != summary.metadata
85    }
86
87    fn diff(
88        _fixture: &'g JsonFixture,
89        (_, summary): &Self::IterItem,
90        existing: Option<&Self::Existing>,
91    ) -> String {
92        // Need to make this a static to allow lifetimes to work out.
93        static EMPTY_SUMMARY: Lazy<Summary> = Lazy::new(Summary::default);
94
95        let existing = match existing {
96            Some(summary) => summary,
97            None => &*EMPTY_SUMMARY,
98        };
99
100        let diff = SummaryDiff::new(existing, summary);
101        format!("{}", diff.report())
102    }
103
104    fn write_to_string(
105        fixture: &'g JsonFixture,
106        (_, summary): &Self::IterItem,
107        out: &mut String,
108    ) -> Result<()> {
109        writeln!(
110            out,
111            "# This summary was @generated. To regenerate, run:\n\
112             #   cargo run -p fixture-manager -- generate-summaries --fixture {}\n",
113            fixture.name()
114        )?;
115
116        summary.write_to_string(out)?;
117        Ok(())
118    }
119}