Skip to main content

rustledger_ops/
enrichment.rs

1//! Shared types for operation results.
2//!
3//! These types describe the outcome of directive operations — how a
4//! categorization was determined, what alternatives were considered,
5//! and how confident the system is in its result.
6
7/// How a categorization was determined.
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub enum CategorizationMethod {
10    /// Matched a user-defined rule (substring or regex).
11    Rule,
12    /// Matched a built-in merchant dictionary entry.
13    MerchantDict,
14    /// ML model prediction.
15    Ml,
16    /// LLM suggestion via MCP.
17    Llm,
18    /// Fell back to default account (no match found).
19    Default,
20    /// User manually assigned.
21    Manual,
22}
23
24impl CategorizationMethod {
25    /// Returns the string representation used in directive metadata.
26    #[must_use]
27    pub const fn as_meta_value(&self) -> &'static str {
28        match self {
29            Self::Rule => "rule",
30            Self::MerchantDict => "merchant-dict",
31            Self::Ml => "ml",
32            Self::Llm => "llm",
33            Self::Default => "default",
34            Self::Manual => "manual",
35        }
36    }
37}
38
39/// An alternative categorization with its confidence score.
40#[derive(Debug, Clone)]
41pub struct Alternative {
42    /// The account that could have been chosen.
43    pub account: String,
44    /// Confidence score for this alternative (0.0 to 1.0).
45    pub confidence: f64,
46    /// How this alternative was determined.
47    pub method: CategorizationMethod,
48}
49
50/// Enrichment metadata for a single directive, produced by operations.
51#[derive(Debug, Clone)]
52pub struct Enrichment {
53    /// Index of the directive this enrichment applies to.
54    pub directive_index: usize,
55    /// Confidence score for the primary categorization (0.0 to 1.0).
56    pub confidence: f64,
57    /// How the primary categorization was determined.
58    pub method: CategorizationMethod,
59    /// Other possible categorizations, sorted by confidence descending.
60    pub alternatives: Vec<Alternative>,
61    /// Stable fingerprint for deduplication (if computed).
62    pub fingerprint: Option<crate::fingerprint::Fingerprint>,
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68
69    #[test]
70    fn categorization_method_meta_values() {
71        assert_eq!(CategorizationMethod::Rule.as_meta_value(), "rule");
72        assert_eq!(
73            CategorizationMethod::MerchantDict.as_meta_value(),
74            "merchant-dict"
75        );
76        assert_eq!(CategorizationMethod::Ml.as_meta_value(), "ml");
77        assert_eq!(CategorizationMethod::Llm.as_meta_value(), "llm");
78        assert_eq!(CategorizationMethod::Default.as_meta_value(), "default");
79        assert_eq!(CategorizationMethod::Manual.as_meta_value(), "manual");
80    }
81}