mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-06-10 06:53:33 -07:00
133 lines
4.4 KiB
Rust
133 lines
4.4 KiB
Rust
use std::{collections::HashSet, path::PathBuf};
|
|
|
|
use brk_error::{Error, Result};
|
|
use brk_rpc::{Auth, Client};
|
|
|
|
use crate::path::Path;
|
|
|
|
pub struct Args {
|
|
pub selector: String,
|
|
pub paths: Vec<Path>,
|
|
pub pretty: bool,
|
|
pub compact: bool,
|
|
bitcoindir: Option<PathBuf>,
|
|
blocksdir: Option<PathBuf>,
|
|
rpcconnect: Option<String>,
|
|
rpcport: Option<u16>,
|
|
rpccookiefile: Option<PathBuf>,
|
|
rpcuser: Option<String>,
|
|
rpcpassword: Option<String>,
|
|
}
|
|
|
|
impl Args {
|
|
pub fn parse(raw: Vec<String>) -> Result<Self> {
|
|
let mut pretty = false;
|
|
let mut compact = false;
|
|
let mut bitcoindir = None;
|
|
let mut blocksdir = None;
|
|
let mut rpcconnect = None;
|
|
let mut rpcport = None;
|
|
let mut rpccookiefile = None;
|
|
let mut rpcuser = None;
|
|
let mut rpcpassword = None;
|
|
let mut positional: Vec<String> = Vec::new();
|
|
let mut iter = raw.into_iter();
|
|
while let Some(a) = iter.next() {
|
|
if a == "-p" || a == "--pretty" {
|
|
pretty = true;
|
|
continue;
|
|
}
|
|
if a == "-c" || a == "--compact" {
|
|
compact = true;
|
|
continue;
|
|
}
|
|
if let Some(rest) = a.strip_prefix("--") {
|
|
let (key, value) = match rest.split_once('=') {
|
|
Some((k, v)) => (k.to_string(), v.to_string()),
|
|
None => (
|
|
rest.to_string(),
|
|
iter.next()
|
|
.ok_or_else(|| Error::Parse(format!("--{rest} requires a value")))?,
|
|
),
|
|
};
|
|
match key.as_str() {
|
|
"bitcoindir" => bitcoindir = Some(PathBuf::from(value)),
|
|
"blocksdir" => blocksdir = Some(PathBuf::from(value)),
|
|
"rpcconnect" => rpcconnect = Some(value),
|
|
"rpcport" => {
|
|
rpcport = Some(value.parse().map_err(|_| {
|
|
Error::Parse(format!("--rpcport: '{value}' is not a valid port"))
|
|
})?);
|
|
}
|
|
"rpccookiefile" => rpccookiefile = Some(PathBuf::from(value)),
|
|
"rpcuser" => rpcuser = Some(value),
|
|
"rpcpassword" => rpcpassword = Some(value),
|
|
other => return Err(Error::Parse(format!("unknown flag --{other}"))),
|
|
}
|
|
continue;
|
|
}
|
|
if a.starts_with('-') {
|
|
return Err(Error::Parse(format!("unknown flag {a}")));
|
|
}
|
|
positional.push(a);
|
|
}
|
|
|
|
let mut iter = positional.into_iter();
|
|
let selector = iter
|
|
.next()
|
|
.ok_or_else(|| Error::Parse("missing selector".into()))?;
|
|
let paths: Vec<Path> = iter.map(|f| Path::parse(&f)).collect::<Result<_>>()?;
|
|
let mut seen = HashSet::with_capacity(paths.len());
|
|
for p in &paths {
|
|
if !seen.insert(p.raw.as_str()) {
|
|
return Err(Error::Parse(format!("duplicate field '{}'", p.raw)));
|
|
}
|
|
}
|
|
Ok(Self {
|
|
selector,
|
|
paths,
|
|
pretty,
|
|
compact,
|
|
bitcoindir,
|
|
blocksdir,
|
|
rpcconnect,
|
|
rpcport,
|
|
rpccookiefile,
|
|
rpcuser,
|
|
rpcpassword,
|
|
})
|
|
}
|
|
|
|
pub fn bitcoin_dir(&self) -> PathBuf {
|
|
self.bitcoindir
|
|
.clone()
|
|
.unwrap_or_else(Client::default_bitcoin_path)
|
|
}
|
|
|
|
pub fn blocks_dir(&self) -> PathBuf {
|
|
self.blocksdir
|
|
.clone()
|
|
.unwrap_or_else(|| self.bitcoin_dir().join("blocks"))
|
|
}
|
|
|
|
pub fn rpc(&self) -> Result<Client> {
|
|
let host = self.rpcconnect.as_deref().unwrap_or("localhost");
|
|
let port = self.rpcport.unwrap_or(8332);
|
|
let url = format!("http://{host}:{port}");
|
|
let cookie = self
|
|
.rpccookiefile
|
|
.clone()
|
|
.unwrap_or_else(|| self.bitcoin_dir().join(".cookie"));
|
|
let auth = if cookie.is_file() {
|
|
Auth::CookieFile(cookie)
|
|
} else if let (Some(u), Some(p)) = (self.rpcuser.as_deref(), self.rpcpassword.as_deref()) {
|
|
Auth::UserPass(u.to_string(), p.to_string())
|
|
} else {
|
|
return Err(Error::Parse(
|
|
"no RPC auth: cookie file missing and --rpcuser/--rpcpassword not set".into(),
|
|
));
|
|
};
|
|
Client::new(&url, auth)
|
|
}
|
|
}
|