whitaker_common/rstest/argument_fingerprint.rs
1//! Pure argument fingerprints for repeated `rstest` helper-call evidence.
2
3/// A lowered argument value that participates in helper-call grouping.
4#[derive(Clone, Debug, PartialEq, Eq, Hash)]
5pub enum ArgAtom {
6 /// An argument supplied by an `rstest` fixture-local parameter.
7 FixtureLocal { name: String },
8 /// A stable literal argument, stored as canonical source text.
9 ConstLit { text: String },
10 /// A stable constant path, stored as a canonical definition path.
11 ConstPath { def_path: String },
12 /// A present argument shape that later lowering does not support.
13 Unsupported,
14}
15
16impl ArgAtom {
17 /// Builds a fixture-local argument atom.
18 ///
19 /// # Examples
20 ///
21 /// ```
22 /// use whitaker_common::rstest::ArgAtom;
23 ///
24 /// let atom = ArgAtom::fixture_local("db");
25 /// assert_eq!(atom, ArgAtom::FixtureLocal { name: "db".to_string() });
26 /// ```
27 #[must_use]
28 pub fn fixture_local(name: impl Into<String>) -> Self {
29 Self::FixtureLocal { name: name.into() }
30 }
31
32 /// Builds a stable literal argument atom.
33 ///
34 /// # Examples
35 ///
36 /// ```
37 /// use whitaker_common::rstest::ArgAtom;
38 ///
39 /// let atom = ArgAtom::const_lit("42");
40 /// assert_eq!(atom, ArgAtom::ConstLit { text: "42".to_string() });
41 /// ```
42 #[must_use]
43 pub fn const_lit(text: impl Into<String>) -> Self {
44 Self::ConstLit { text: text.into() }
45 }
46
47 /// Builds a stable constant-path argument atom.
48 ///
49 /// # Examples
50 ///
51 /// ```
52 /// use whitaker_common::rstest::ArgAtom;
53 ///
54 /// let atom = ArgAtom::const_path("crate::defaults::TIMEOUT");
55 /// assert_eq!(
56 /// atom,
57 /// ArgAtom::ConstPath {
58 /// def_path: "crate::defaults::TIMEOUT".to_string(),
59 /// },
60 /// );
61 /// ```
62 #[must_use]
63 pub fn const_path(def_path: impl Into<String>) -> Self {
64 Self::ConstPath {
65 def_path: def_path.into(),
66 }
67 }
68
69 /// Builds an explicit unsupported argument atom.
70 ///
71 /// # Examples
72 ///
73 /// ```
74 /// use whitaker_common::rstest::ArgAtom;
75 ///
76 /// assert_eq!(ArgAtom::unsupported(), ArgAtom::Unsupported);
77 /// ```
78 #[must_use]
79 pub const fn unsupported() -> Self {
80 Self::Unsupported
81 }
82}
83
84/// A positional fingerprint for one helper-call argument list.
85#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
86pub struct ArgFingerprint {
87 atoms: Vec<ArgAtom>,
88}
89
90impl ArgFingerprint {
91 /// Builds a fingerprint from argument atoms in call-site order.
92 ///
93 /// # Examples
94 ///
95 /// ```
96 /// use whitaker_common::rstest::{ArgAtom, ArgFingerprint};
97 ///
98 /// let fingerprint = ArgFingerprint::new([
99 /// ArgAtom::fixture_local("db"),
100 /// ArgAtom::const_lit("42"),
101 /// ]);
102 ///
103 /// assert_eq!(fingerprint.atoms().len(), 2);
104 /// ```
105 #[must_use]
106 pub fn new<I>(atoms: I) -> Self
107 where
108 I: IntoIterator<Item = ArgAtom>,
109 {
110 Self {
111 atoms: atoms.into_iter().collect(),
112 }
113 }
114
115 /// Returns the stored atoms in positional order.
116 #[must_use]
117 pub fn atoms(&self) -> &[ArgAtom] {
118 &self.atoms
119 }
120
121 /// Consumes the fingerprint and returns the stored atoms.
122 #[must_use]
123 pub fn into_atoms(self) -> Vec<ArgAtom> {
124 self.atoms
125 }
126}