r402_core/scheme/server.rs
1//! Server-side scheme abstractions.
2//!
3//! A resource server (seller) uses a [`SchemeServer`] to translate
4//! human-readable prices like `"$0.01"` into a full
5//! [`PaymentRequirements`] with scheme-specific `extra` data filled in.
6
7use compact_str::CompactString;
8
9use crate::chain::ChainId;
10use crate::scheme::sealed::Sealed;
11use crate::wire::PaymentRequirements;
12
13/// A resolved asset amount ready for insertion into
14/// [`PaymentRequirements`].
15#[derive(Debug, Clone)]
16pub struct AssetAmount {
17 /// Token asset address / mint (wire-level string).
18 pub asset: CompactString,
19 /// Amount in the token's smallest unit, stringified.
20 pub amount: CompactString,
21}
22
23/// Errors emitted by a [`SchemeServer`].
24#[derive(Debug, thiserror::Error)]
25#[non_exhaustive]
26pub enum SchemeServerError {
27 /// The price could not be parsed.
28 #[error("invalid price: {0}")]
29 InvalidPrice(String),
30 /// The chain or asset is not configured.
31 #[error("unsupported chain or asset: {0}")]
32 UnsupportedChain(String),
33 /// Any other server-side failure.
34 #[error("{0}")]
35 Other(String),
36}
37
38/// Server-side scheme interface.
39///
40/// Sealed: only crates inside this workspace may implement it.
41pub trait SchemeServer: super::SchemeId + Sealed + Send + Sync {
42 /// Parses a human-readable price into the scheme's internal amount form.
43 ///
44 /// # Errors
45 ///
46 /// Returns [`SchemeServerError`] when the price or chain is not supported.
47 fn parse_price(&self, price: &str, network: &ChainId)
48 -> Result<AssetAmount, SchemeServerError>;
49
50 /// Fills in scheme-specific `extra` data on the requirements.
51 ///
52 /// Default implementation returns the input unchanged.
53 fn enhance_requirements(&self, requirements: PaymentRequirements) -> PaymentRequirements {
54 requirements
55 }
56
57 /// Builds a complete [`PaymentRequirements`] from a price.
58 ///
59 /// # Errors
60 ///
61 /// Returns [`SchemeServerError`] when the price cannot be resolved.
62 fn build_requirements(
63 &self,
64 price: &str,
65 network: &ChainId,
66 pay_to: &str,
67 max_timeout_seconds: u64,
68 ) -> Result<PaymentRequirements, SchemeServerError> {
69 let AssetAmount { asset, amount } = self.parse_price(price, network)?;
70 let base = PaymentRequirements {
71 scheme: CompactString::from(self.scheme()),
72 network: network.clone(),
73 amount,
74 pay_to: CompactString::from(pay_to),
75 max_timeout_seconds,
76 asset,
77 extra: None,
78 };
79 Ok(self.enhance_requirements(base))
80 }
81}