Skip to main content

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}