Skip to main content

r402_client/
policy.rs

1//! Filters applied to the candidate list before selection.
2
3use compact_str::CompactString;
4use r402_protocol::ChainIdPattern;
5
6use crate::candidate::PaymentCandidate;
7
8/// Filter applied to the candidate list before selection.
9pub trait PaymentPolicy: Send + Sync {
10    /// Returns the subset of candidates that pass this policy.
11    fn apply<'a>(&self, candidates: Vec<&'a PaymentCandidate>) -> Vec<&'a PaymentCandidate>;
12}
13
14/// Keeps only candidates whose chain matches one of the patterns.
15#[derive(Debug, Default)]
16pub struct NetworkPolicy(Vec<ChainIdPattern>);
17
18impl NetworkPolicy {
19    /// Constructs the policy from a list of patterns.
20    #[must_use]
21    pub fn new<P: Into<Vec<ChainIdPattern>>>(patterns: P) -> Self {
22        Self(patterns.into())
23    }
24}
25
26impl PaymentPolicy for NetworkPolicy {
27    fn apply<'a>(&self, candidates: Vec<&'a PaymentCandidate>) -> Vec<&'a PaymentCandidate> {
28        candidates
29            .into_iter()
30            .filter(|c| self.0.iter().any(|p| p.matches(&c.chain_id)))
31            .collect()
32    }
33}
34
35/// Keeps only candidates whose scheme name is in the allow-list.
36#[derive(Debug, Default)]
37pub struct SchemePolicy(Vec<CompactString>);
38
39impl SchemePolicy {
40    /// Constructs the policy from an iterator of names.
41    #[must_use]
42    pub fn new<S: Into<CompactString>, I: IntoIterator<Item = S>>(schemes: I) -> Self {
43        Self(schemes.into_iter().map(Into::into).collect())
44    }
45}
46
47impl PaymentPolicy for SchemePolicy {
48    fn apply<'a>(&self, candidates: Vec<&'a PaymentCandidate>) -> Vec<&'a PaymentCandidate> {
49        candidates
50            .into_iter()
51            .filter(|c| self.0.iter().any(|s| s.as_str() == c.scheme.as_str()))
52            .collect()
53    }
54}
55
56/// Keeps only candidates whose amount is at most the configured bound.
57#[derive(Debug, Clone, Copy)]
58pub struct MaxAmountPolicy(pub u128);
59
60impl PaymentPolicy for MaxAmountPolicy {
61    fn apply<'a>(&self, candidates: Vec<&'a PaymentCandidate>) -> Vec<&'a PaymentCandidate> {
62        candidates
63            .into_iter()
64            .filter(|c| c.amount.parse::<u128>().is_ok_and(|a| a <= self.0))
65            .collect()
66    }
67}