hakari/explain/
simplify.rs

1// Copyright (c) The cargo-guppy Contributors
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4use itertools::Itertools;
5#[cfg(feature = "cli-support")]
6use owo_colors::{OwoColorize, Style};
7#[cfg(feature = "cli-support")]
8use std::fmt;
9use std::{collections::BTreeSet, hash::Hash};
10
11#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
12pub(super) enum Simple<T> {
13    Any,
14    Some(T),
15}
16
17impl<T> Simple<T> {
18    #[cfg(feature = "cli-support")]
19    pub(super) fn display_with<'simple, F>(
20        &'simple self,
21        star_style: &'simple Style,
22        display_fn: F,
23    ) -> SimpleDisplay<'simple, T, F>
24    where
25        F: Fn(&T, &mut fmt::Formatter) -> fmt::Result,
26    {
27        SimpleDisplay {
28            simple: self,
29            star_style,
30            display_fn,
31        }
32    }
33}
34
35#[cfg(feature = "cli-support")]
36pub(super) struct SimpleDisplay<'simple, T, F> {
37    simple: &'simple Simple<T>,
38    star_style: &'simple Style,
39    display_fn: F,
40}
41
42#[cfg(feature = "cli-support")]
43impl<T, F> fmt::Display for SimpleDisplay<'_, T, F>
44where
45    F: Fn(&T, &mut fmt::Formatter) -> fmt::Result,
46{
47    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48        match self.simple {
49            Simple::Any => write!(f, "{}", "*".style(*self.star_style)),
50            Simple::Some(val) => (self.display_fn)(val, f),
51        }
52    }
53}
54
55pub(super) fn simplify3<A, B, C>(
56    input: &BTreeSet<(A, B, C)>,
57    (n_a, n_b, n_c): (usize, usize, usize),
58) -> Vec<(Simple<A>, Simple<B>, Simple<C>)>
59where
60    A: Eq + Hash + Ord + Clone,
61    B: Eq + Hash + Ord + Clone,
62    C: Eq + Hash + Ord + Clone,
63{
64    // Do a super janky simplification right now
65    // TODO: replace with a proper logic minimizer?
66
67    if input.len() == (n_a * n_b * n_c) {
68        return vec![(Simple::Any, Simple::Any, Simple::Any)];
69    }
70
71    let mut res = vec![];
72    let group_map = input.iter().map(|(a, b, c)| (a, (b, c))).into_group_map();
73
74    // It would be nice if into_group_map returned anything but HashMap:
75    // https://github.com/rust-itertools/itertools/issues/520
76    for (a, val) in group_map.into_iter().sorted() {
77        if val.len() == n_b * n_c {
78            res.push((Simple::Some(a.clone()), Simple::Any, Simple::Any));
79        } else {
80            for (b, val) in val.into_iter().into_group_map().into_iter().sorted() {
81                if val.len() == n_c {
82                    res.push((
83                        Simple::Some(a.clone()),
84                        Simple::Some(b.clone()),
85                        Simple::Any,
86                    ));
87                } else {
88                    for c in val {
89                        res.push((
90                            Simple::Some(a.clone()),
91                            Simple::Some(b.clone()),
92                            Simple::Some(c.clone()),
93                        ));
94                    }
95                }
96            }
97        }
98    }
99
100    res
101}
102
103pub(super) fn simplify1<A>(input: &BTreeSet<A>, n_a: usize) -> Vec<Simple<A>>
104where
105    A: Eq + Hash + Ord + Clone,
106{
107    if input.len() == n_a {
108        vec![Simple::Any]
109    } else {
110        input.iter().map(|a| Simple::Some(a.clone())).collect()
111    }
112}