• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

payjoin / rust-payjoin / 15441191882

04 Jun 2025 11:33AM UTC coverage: 84.857% (+0.02%) from 84.833%
15441191882

push

github

web-flow
Sender generic over typestate (#728)

This commit updates the `v2::Sender` to be generic over its
typestate (e.g., `V2GetContext`, `V2PostContext`, etc.). The
motivation is outlined in
https://github.com/0xBEEFCAF3/rust-payjoin/commit/c8b860d95

This commit renames the original `Sender` struct to `WithReplyKey`.
This type now represents the v2::sender session once the HPKE context
has been
 created. The `Sender` name is re-purposed for the new generic wrapper
 over typestate.

For the UniFFI exported receiver we monomorphize the exported structs
over a
specific typestate. UniFFI doesn't support exporting generic structs, so
 we expose a concrete type to the FFI.

57 of 60 new or added lines in 6 files covered. (95.0%)

1 existing line in 1 file now uncovered.

6635 of 7819 relevant lines covered (84.86%)

593.4 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.59
/payjoin-cli/src/db/v2.rs
1
use std::sync::Arc;
2

3
use bitcoincore_rpc::jsonrpc::serde_json;
4
use payjoin::persist::{Persister, Value};
5
use payjoin::receive::v2::{Receiver, ReceiverToken, WithContext};
6
use payjoin::send::v2::{Sender, SenderToken, WithReplyKey};
7
use sled::Tree;
8
use url::Url;
9

10
use super::*;
11

12
pub(crate) struct SenderPersister(Arc<Database>);
13
impl SenderPersister {
14
    pub fn new(db: Arc<Database>) -> Self { Self(db) }
1✔
15
}
16

17
impl Persister<Sender<WithReplyKey>> for SenderPersister {
18
    type Token = SenderToken;
19
    type Error = crate::db::error::Error;
20
    fn save(
1✔
21
        &mut self,
1✔
22
        value: Sender<WithReplyKey>,
1✔
23
    ) -> std::result::Result<SenderToken, Self::Error> {
1✔
24
        let send_tree = self.0 .0.open_tree("send_sessions")?;
1✔
25
        let key = value.key();
1✔
26
        let value = serde_json::to_vec(&value).map_err(Error::Serialize)?;
1✔
27
        send_tree.insert(key.clone(), value.as_slice())?;
1✔
28
        send_tree.flush()?;
1✔
29
        Ok(key)
1✔
30
    }
1✔
31

32
    fn load(&self, key: SenderToken) -> std::result::Result<Sender<WithReplyKey>, Self::Error> {
1✔
33
        let send_tree = self.0 .0.open_tree("send_sessions")?;
1✔
34
        let value = send_tree.get(key.as_ref())?.ok_or(Error::NotFound(key.to_string()))?;
1✔
35
        serde_json::from_slice(&value).map_err(Error::Deserialize)
1✔
36
    }
1✔
37
}
38

39
pub(crate) struct ReceiverPersister(Arc<Database>);
40
impl ReceiverPersister {
41
    pub fn new(db: Arc<Database>) -> Self { Self(db) }
1✔
42
}
43

44
impl Persister<Receiver<WithContext>> for ReceiverPersister {
45
    type Token = ReceiverToken;
46
    type Error = crate::db::error::Error;
47
    fn save(
1✔
48
        &mut self,
1✔
49
        value: Receiver<WithContext>,
1✔
50
    ) -> std::result::Result<ReceiverToken, Self::Error> {
1✔
51
        let recv_tree = self.0 .0.open_tree("recv_sessions")?;
1✔
52
        let key = value.key();
1✔
53
        let value = serde_json::to_vec(&value).map_err(Error::Serialize)?;
1✔
54
        recv_tree.insert(key.clone(), value.as_slice())?;
1✔
55
        recv_tree.flush()?;
1✔
56
        Ok(key)
1✔
57
    }
1✔
58
    fn load(&self, key: ReceiverToken) -> std::result::Result<Receiver<WithContext>, Self::Error> {
1✔
59
        let recv_tree = self.0 .0.open_tree("recv_sessions")?;
1✔
60
        let value = recv_tree.get(key.as_ref())?.ok_or(Error::NotFound(key.to_string()))?;
1✔
61
        serde_json::from_slice(&value).map_err(Error::Deserialize)
1✔
62
    }
1✔
63
}
64

65
impl Database {
66
    pub(crate) fn get_recv_sessions(&self) -> Result<Vec<Receiver<WithContext>>> {
1✔
67
        let recv_tree = self.0.open_tree("recv_sessions")?;
1✔
68
        let mut sessions = Vec::new();
1✔
69
        for item in recv_tree.iter() {
1✔
70
            let (_, value) = item?;
1✔
71
            let session: Receiver<WithContext> =
1✔
72
                serde_json::from_slice(&value).map_err(Error::Deserialize)?;
1✔
73
            sessions.push(session);
1✔
74
        }
75
        Ok(sessions)
1✔
76
    }
1✔
77

78
    pub(crate) fn clear_recv_session(&self) -> Result<()> {
1✔
79
        let recv_tree: Tree = self.0.open_tree("recv_sessions")?;
1✔
80
        recv_tree.clear()?;
1✔
81
        recv_tree.flush()?;
1✔
82
        Ok(())
1✔
83
    }
1✔
84

85
    pub(crate) fn get_send_sessions(&self) -> Result<Vec<Sender<WithReplyKey>>> {
1✔
86
        let send_tree: Tree = self.0.open_tree("send_sessions")?;
1✔
87
        let mut sessions = Vec::new();
1✔
88
        for item in send_tree.iter() {
1✔
89
            let (_, value) = item?;
×
NEW
90
            let session: Sender<WithReplyKey> =
×
NEW
91
                serde_json::from_slice(&value).map_err(Error::Deserialize)?;
×
UNCOV
92
            sessions.push(session);
×
93
        }
94
        Ok(sessions)
1✔
95
    }
1✔
96

97
    pub(crate) fn get_send_session(&self, pj_url: &Url) -> Result<Option<Sender<WithReplyKey>>> {
2✔
98
        let send_tree = self.0.open_tree("send_sessions")?;
2✔
99
        if let Some(val) = send_tree.get(pj_url.as_str())? {
2✔
100
            let session: Sender<WithReplyKey> =
1✔
101
                serde_json::from_slice(&val).map_err(Error::Deserialize)?;
1✔
102
            Ok(Some(session))
1✔
103
        } else {
104
            Ok(None)
1✔
105
        }
106
    }
2✔
107

108
    pub(crate) fn clear_send_session(&self, pj_url: &Url) -> Result<()> {
1✔
109
        let send_tree: Tree = self.0.open_tree("send_sessions")?;
1✔
110
        send_tree.remove(pj_url.as_str())?;
1✔
111
        send_tree.flush()?;
1✔
112
        Ok(())
1✔
113
    }
1✔
114
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc