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

payjoin / rust-payjoin / 12856536243

19 Jan 2025 07:30PM UTC coverage: 60.548% (+0.06%) from 60.489%
12856536243

Pull #491

github

web-flow
Merge 13bf515d5 into 4bbac3446
Pull Request #491: use FeeRate for max_fee_rate arg, and default it to BROADCAST_MIN

7 of 13 new or added lines in 6 files covered. (53.85%)

5 existing lines in 1 file now uncovered.

2939 of 4854 relevant lines covered (60.55%)

953.84 hits per line

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

0.0
/payjoin-cli/src/app/config.rs
1
use std::path::PathBuf;
2

3
use anyhow::Result;
4
use clap::ArgMatches;
5
use config::{Config, ConfigError, File, FileFormat};
6
use payjoin::bitcoin::FeeRate;
7
use serde::Deserialize;
8
use url::Url;
9

10
use crate::db;
11

12
#[derive(Debug, Clone, Deserialize)]
×
13
pub struct AppConfig {
14
    pub bitcoind_rpchost: Url,
15
    pub bitcoind_cookie: Option<PathBuf>,
16
    pub bitcoind_rpcuser: String,
17
    pub bitcoind_rpcpassword: String,
18
    pub db_path: PathBuf,
19
    // receive-only
20
    pub max_fee_rate: Option<FeeRate>,
21

22
    // v2 only
23
    #[cfg(feature = "v2")]
24
    #[serde(deserialize_with = "deserialize_ohttp_keys_from_path")]
25
    pub ohttp_keys: Option<payjoin::OhttpKeys>,
26
    #[cfg(feature = "v2")]
27
    pub ohttp_relay: Url,
28
    #[cfg(feature = "v2")]
29
    pub pj_directory: Url,
30

31
    // v1 receive-only
32
    #[cfg(not(feature = "v2"))]
33
    pub port: u16,
34
    #[cfg(not(feature = "v2"))]
35
    pub pj_endpoint: Url,
36
}
37

38
impl AppConfig {
39
    pub(crate) fn new(matches: &ArgMatches) -> Result<Self, ConfigError> {
×
40
        let builder = Config::builder()
×
41
            .set_default("bitcoind_rpchost", "http://localhost:18443")?
×
42
            .set_override_option(
×
43
                "bitcoind_rpchost",
×
44
                matches.get_one::<Url>("rpchost").map(|s| s.as_str()),
×
45
            )?
×
46
            .set_default("bitcoind_cookie", None::<String>)?
×
47
            .set_override_option(
×
48
                "bitcoind_cookie",
×
49
                matches.get_one::<String>("cookie_file").map(|s| s.as_str()),
×
50
            )?
×
51
            .set_default("bitcoind_rpcuser", "bitcoin")?
×
52
            .set_override_option(
×
53
                "bitcoind_rpcuser",
×
54
                matches.get_one::<String>("rpcuser").map(|s| s.as_str()),
×
55
            )?
×
56
            .set_default("bitcoind_rpcpassword", "")?
×
57
            .set_override_option(
×
58
                "bitcoind_rpcpassword",
×
59
                matches.get_one::<String>("rpcpassword").map(|s| s.as_str()),
×
60
            )?
×
61
            .set_default("db_path", db::DB_PATH)?
×
62
            .set_override_option(
×
63
                "db_path",
×
64
                matches.get_one::<String>("db_path").map(|s| s.as_str()),
×
65
            )?
×
66
            // Subcommand defaults without which file serialization fails.
67
            .set_default("port", "3000")?
×
68
            .set_default("pj_endpoint", "https://localhost:3000")?
×
69
            .add_source(File::new("config.toml", FileFormat::Toml).required(false));
×
70

71
        #[cfg(feature = "v2")]
72
        let builder = builder
×
73
            .set_override_option(
×
74
                "ohttp_relay",
×
75
                matches.get_one::<Url>("ohttp_relay").map(|s| s.as_str()),
×
76
            )?
×
77
            .set_default("pj_directory", "https://payjo.in")?
×
78
            .set_default("ohttp_keys", None::<String>)?;
×
79

80
        let builder = match matches.subcommand() {
×
81
            Some(("send", _)) => builder,
×
82
            Some(("receive", matches)) => {
×
83
                #[cfg(not(feature = "v2"))]
84
                let builder = {
×
85
                    let port = matches
×
86
                        .get_one::<String>("port")
×
87
                        .map(|port| port.parse::<u16>())
×
88
                        .transpose()
×
89
                        .map_err(|_| {
×
90
                            ConfigError::Message("\"port\" must be a valid number".to_string())
×
91
                        })?;
×
92
                    builder.set_override_option("port", port)?.set_override_option(
×
93
                        "pj_endpoint",
×
94
                        matches.get_one::<Url>("pj_endpoint").map(|s| s.as_str()),
×
95
                    )?
×
96
                };
97

98
                #[cfg(feature = "v2")]
99
                let builder = {
×
100
                    builder
×
101
                        .set_override_option(
×
102
                            "pj_directory",
×
103
                            matches.get_one::<Url>("pj_directory").map(|s| s.as_str()),
×
104
                        )?
×
105
                        .set_override_option(
×
106
                            "ohttp_keys",
×
107
                            matches.get_one::<String>("ohttp_keys").map(|s| s.as_str()),
×
108
                        )?
×
109
                };
110

NEW
111
                let max_fee_rate = matches.get_one::<FeeRate>("max_fee_rate");
×
NEW
112
                builder.set_override_option("max_fee_rate", max_fee_rate.map(|f| f.to_string()))?
×
113
            }
114
            #[cfg(feature = "v2")]
115
            Some(("resume", _)) => builder,
×
116
            _ => unreachable!(), // If all subcommands are defined above, anything else is unreachabe!()
×
117
        };
118

119
        let config = builder.build()?;
×
120
        let app_config: AppConfig = config.try_deserialize()?;
×
121
        log::debug!("App config: {:?}", app_config);
×
122
        Ok(app_config)
×
123
    }
×
124
}
125

126
#[cfg(feature = "v2")]
127
fn deserialize_ohttp_keys_from_path<'de, D>(
×
128
    deserializer: D,
×
129
) -> Result<Option<payjoin::OhttpKeys>, D::Error>
×
130
where
×
131
    D: serde::Deserializer<'de>,
×
132
{
×
133
    let path_str: Option<String> = Option::deserialize(deserializer)?;
×
134

135
    match path_str {
×
136
        None => Ok(None),
×
137
        Some(path) => std::fs::read(path)
×
138
            .map_err(|e| serde::de::Error::custom(format!("Failed to read ohttp_keys file: {}", e)))
×
139
            .and_then(|bytes| {
×
140
                payjoin::OhttpKeys::decode(&bytes).map_err(|e| {
×
141
                    serde::de::Error::custom(format!("Failed to decode ohttp keys: {}", e))
×
142
                })
×
143
            })
×
144
            .map(Some),
×
145
    }
146
}
×
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