Skip to content

Commit

Permalink
Use JSON for wallet command output (#1359)
Browse files Browse the repository at this point in the history
  • Loading branch information
raphjaph authored Jan 25, 2023
1 parent 8ff9c7a commit d9e3683
Show file tree
Hide file tree
Showing 21 changed files with 301 additions and 122 deletions.
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ mod rarity;
mod representation;
mod sat;
mod sat_point;
mod subcommand;
pub mod subcommand;
mod tally;
mod templates;

Expand Down
63 changes: 62 additions & 1 deletion src/rarity.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::*;

#[derive(Debug, PartialEq, PartialOrd)]
pub(crate) enum Rarity {
pub enum Rarity {
Common,
Uncommon,
Rare,
Expand Down Expand Up @@ -52,6 +52,40 @@ impl From<Sat> for Rarity {
}
}

impl FromStr for Rarity {
type Err = Error;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"common" => Ok(Self::Common),
"uncommon" => Ok(Self::Uncommon),
"rare" => Ok(Self::Rare),
"epic" => Ok(Self::Epic),
"legendary" => Ok(Self::Legendary),
"mythic" => Ok(Self::Mythic),
_ => Err(anyhow!("invalid rarity: {s}")),
}
}
}

impl Serialize for Rarity {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.collect_str(self)
}
}

impl<'de> Deserialize<'de> for Rarity {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Ok(DeserializeFromStr::deserialize(deserializer)?.0)
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -95,4 +129,31 @@ mod tests {
assert_eq!(Sat(2067187500000000).rarity(), Rarity::Legendary);
assert_eq!(Sat(2067187500000000 + 1).rarity(), Rarity::Common);
}

#[test]
fn from_str_and_deserialize_ok() {
#[track_caller]
fn case(s: &str, expected: Rarity) {
let actual = s.parse::<Rarity>().unwrap();
assert_eq!(actual, expected);
let round_trip = actual.to_string().parse::<Rarity>().unwrap();
assert_eq!(round_trip, expected);
let serialized = serde_json::to_string(&expected).unwrap();
assert!(serde_json::from_str::<Rarity>(&serialized).is_ok());
}

case("common", Rarity::Common);
case("uncommon", Rarity::Uncommon);
case("rare", Rarity::Rare);
case("epic", Rarity::Epic);
case("legendary", Rarity::Legendary);
case("mythic", Rarity::Mythic);
}

#[test]
fn from_str_err() {
"abc".parse::<Rarity>().unwrap_err();

"".parse::<Rarity>().unwrap_err();
}
}
2 changes: 1 addition & 1 deletion src/sat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use super::*;

#[derive(Copy, Clone, Eq, PartialEq, Debug, Display, Ord, PartialOrd, Deserialize, Serialize)]
#[serde(transparent)]
pub(crate) struct Sat(pub(crate) u64);
pub struct Sat(pub(crate) u64);

impl Sat {
pub(crate) const LAST: Self = Self(Self::SUPPLY - 1);
Expand Down
25 changes: 25 additions & 0 deletions src/sat_point.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@ impl Serialize for SatPoint {
}
}

impl<'de> Deserialize<'de> for SatPoint {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Ok(DeserializeFromStr::deserialize(deserializer)?.0)
}
}

impl FromStr for SatPoint {
type Err = Error;

Expand Down Expand Up @@ -87,4 +96,20 @@ mod tests {
.parse::<SatPoint>()
.unwrap_err();
}

#[test]
fn deserialize_ok() {
assert_eq!(
serde_json::from_str::<SatPoint>(
"\"1111111111111111111111111111111111111111111111111111111111111111:1:1\""
)
.unwrap(),
SatPoint {
outpoint: "1111111111111111111111111111111111111111111111111111111111111111:1"
.parse()
.unwrap(),
offset: 1,
}
);
}
}
2 changes: 1 addition & 1 deletion src/subcommand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ mod server;
mod subsidy;
mod supply;
mod traits;
pub(crate) mod wallet;
pub mod wallet;

fn print_json(output: impl Serialize) -> Result {
serde_json::to_writer_pretty(io::stdout(), &output)?;
Expand Down
14 changes: 7 additions & 7 deletions src/subcommand/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,17 @@ use {
transaction_builder::TransactionBuilder,
};

mod balance;
pub mod balance;
pub(crate) mod create;
pub(crate) mod inscribe;
mod inscriptions;
mod outputs;
mod receive;
pub mod inscriptions;
pub mod outputs;
pub mod receive;
mod restore;
mod sats;
mod send;
pub mod sats;
pub mod send;
pub(crate) mod transaction_builder;
mod transactions;
pub mod transactions;

#[derive(Debug, Parser)]
pub(crate) enum Wallet {
Expand Down
7 changes: 6 additions & 1 deletion src/subcommand/wallet/balance.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
use super::*;
use std::collections::BTreeSet;

#[derive(Serialize, Deserialize)]
pub struct Output {
pub cardinal: u64,
}

pub(crate) fn run(options: Options) -> Result {
let index = Index::open(&options)?;
index.update()?;
Expand All @@ -18,7 +23,7 @@ pub(crate) fn run(options: Options) -> Result {
}
}

println!("{}", balance);
print_json(Output { cardinal: balance })?;

Ok(())
}
10 changes: 5 additions & 5 deletions src/subcommand/wallet/inscriptions.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use super::*;

#[derive(Serialize)]
struct Output {
inscription: InscriptionId,
location: SatPoint,
explorer: String,
#[derive(Serialize, Deserialize)]
pub struct Output {
pub inscription: InscriptionId,
pub location: SatPoint,
pub explorer: String,
}

pub(crate) fn run(options: Options) -> Result {
Expand Down
16 changes: 14 additions & 2 deletions src/subcommand/wallet/outputs.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
use super::*;

#[derive(Serialize, Deserialize)]
pub struct Output {
pub output: OutPoint,
pub amount: u64,
}

pub(crate) fn run(options: Options) -> Result {
for (outpoint, amount) in get_unspent_outputs(&options)? {
println!("{outpoint}\t{}", amount.to_sat());
let mut outputs = Vec::new();
for (output, amount) in get_unspent_outputs(&options)? {
outputs.push(Output {
output,
amount: amount.to_sat(),
});
}

print_json(outputs)?;

Ok(())
}
7 changes: 6 additions & 1 deletion src/subcommand/wallet/receive.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
use super::*;

#[derive(Deserialize, Serialize)]
pub struct Output {
pub address: Address,
}

pub(crate) fn run(options: Options) -> Result {
let address = options
.bitcoin_rpc_client_for_wallet_command(false)?
.get_new_address(None, Some(bitcoincore_rpc::json::AddressType::Bech32m))?;

println!("{}", address);
print_json(Output { address })?;

Ok(())
}
34 changes: 30 additions & 4 deletions src/subcommand/wallet/sats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,20 @@ pub(crate) struct Sats {
tsv: Option<PathBuf>,
}

#[derive(Serialize, Deserialize)]
pub struct OutputTsv {
pub sat: String,
pub output: OutPoint,
}

#[derive(Serialize, Deserialize)]
pub struct OutputRare {
pub sat: Sat,
pub output: OutPoint,
pub offset: u64,
pub rarity: Rarity,
}

impl Sats {
pub(crate) fn run(&self, options: Options) -> Result {
let index = Index::open(&options)?;
Expand All @@ -17,17 +31,29 @@ impl Sats {
let utxos = get_unspent_output_ranges(&options, &index)?;

if let Some(path) = &self.tsv {
for (output, sat) in sats_from_tsv(
let mut output = Vec::new();
for (outpoint, sat) in sats_from_tsv(
utxos,
&fs::read_to_string(path)
.with_context(|| format!("I/O error reading `{}`", path.display()))?,
)? {
println!("{output}\t{sat}");
output.push(OutputTsv {
sat: sat.into(),
output: outpoint,
});
}
print_json(output)?;
} else {
for (output, sat, offset, rarity) in rare_sats(utxos) {
println!("{output}\t{sat}\t{offset}\t{rarity}");
let mut output = Vec::new();
for (outpoint, sat, offset, rarity) in rare_sats(utxos) {
output.push(OutputRare {
sat,
output: outpoint,
offset,
rarity,
});
}
print_json(output)?;
}

Ok(())
Expand Down
7 changes: 6 additions & 1 deletion src/subcommand/wallet/send.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ pub(crate) struct Send {
fee_rate: FeeRate,
}

#[derive(Serialize, Deserialize)]
pub struct Output {
pub transaction: Txid,
}

impl Send {
pub(crate) fn run(self, options: Options) -> Result {
let client = options.bitcoin_rpc_client_for_wallet_command(false)?;
Expand Down Expand Up @@ -62,7 +67,7 @@ impl Send {
let txid =
client.send_to_address(&self.address, amount, None, None, None, None, None, None)?;

println!("{txid}");
print_json(Output { transaction: txid })?;

return Ok(());
}
Expand Down
14 changes: 13 additions & 1 deletion src/subcommand/wallet/transactions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,15 @@ pub(crate) struct Transactions {
limit: Option<u16>,
}

#[derive(Serialize, Deserialize)]
pub struct Output {
pub transaction: Txid,
pub confirmations: i32,
}

impl Transactions {
pub(crate) fn run(self, options: Options) -> Result {
let mut output = Vec::new();
for tx in options
.bitcoin_rpc_client_for_wallet_command(false)?
.list_transactions(
Expand All @@ -17,9 +24,14 @@ impl Transactions {
None,
)?
{
println!("{}\t{}", tx.info.txid, tx.info.confirmations);
output.push(Output {
transaction: tx.info.txid,
confirmations: tx.info.confirmations,
});
}

print_json(output)?;

Ok(())
}
}
2 changes: 1 addition & 1 deletion tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use {
self::{command_builder::CommandBuilder, expected::Expected, test_server::TestServer},
bip39::Mnemonic,
bitcoin::{blockdata::constants::COIN_VALUE, Address, Network, OutPoint, Txid},
bitcoin::{blockdata::constants::COIN_VALUE, Network, OutPoint, Txid},
executable_path::executable_path,
pretty_assertions::assert_eq as pretty_assert_eq,
regex::Regex,
Expand Down
Loading

0 comments on commit d9e3683

Please sign in to comment.