1use super::*;
2
3#[derive(Serialize, Eq, PartialEq, Deserialize, Debug)]
4pub struct CompactOutput {
5 pub inscriptions: Vec<CompactInscription>,
6 pub runestone: Option<Artifact>,
7}
8
9#[derive(Serialize, Eq, PartialEq, Deserialize, Debug)]
10pub struct RawOutput {
11 pub inscriptions: Vec<ParsedEnvelope>,
12 pub runestone: Option<Artifact>,
13}
14
15#[derive(Serialize, Eq, PartialEq, Deserialize, Debug)]
16#[serde_with::skip_serializing_none]
17pub struct CompactInscription {
18 pub body: Option<String>,
19 pub content_encoding: Option<String>,
20 pub content_type: Option<String>,
21 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
22 pub duplicate_field: bool,
23 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
24 pub incomplete_field: bool,
25 pub metadata: Option<String>,
26 pub metaprotocol: Option<String>,
27 #[serde(default, skip_serializing_if = "Vec::is_empty")]
28 pub parents: Vec<InscriptionId>,
29 pub pointer: Option<u64>,
30 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
31 pub unrecognized_even_field: bool,
32}
33
34impl TryFrom<Inscription> for CompactInscription {
35 type Error = Error;
36
37 fn try_from(inscription: Inscription) -> Result<Self> {
38 Ok(Self {
39 content_encoding: inscription
40 .content_encoding()
41 .map(|header_value| header_value.to_str().map(str::to_string))
42 .transpose()?,
43 content_type: inscription.content_type().map(str::to_string),
44 metaprotocol: inscription.metaprotocol().map(str::to_string),
45 parents: inscription.parents(),
46 pointer: inscription.pointer(),
47 body: inscription.body.map(hex::encode),
48 duplicate_field: inscription.duplicate_field,
49 incomplete_field: inscription.incomplete_field,
50 metadata: inscription.metadata.map(hex::encode),
51 unrecognized_even_field: inscription.unrecognized_even_field,
52 })
53 }
54}
55
56#[derive(Debug, Parser)]
57pub(crate) struct Decode {
58 #[arg(
59 long,
60 conflicts_with = "file",
61 help = "Fetch transaction with <TXID> from Bitcoin Core."
62 )]
63 txid: Option<Txid>,
64 #[arg(long, conflicts_with = "txid", help = "Load transaction from <FILE>.")]
65 file: Option<PathBuf>,
66 #[arg(
67 long,
68 help = "Serialize inscriptions in a compact, human-readable format."
69 )]
70 compact: bool,
71}
72
73impl Decode {
74 pub(crate) fn run(self, settings: Settings) -> SubcommandResult {
75 let transaction = if let Some(txid) = self.txid {
76 settings
77 .bitcoin_rpc_client(None)?
78 .get_raw_transaction(&txid, None)?
79 } else if let Some(file) = self.file {
80 Transaction::consensus_decode(&mut fs::File::open(file)?)?
81 } else {
82 Transaction::consensus_decode(&mut io::stdin())?
83 };
84
85 let inscriptions = ParsedEnvelope::from_transaction(&transaction);
86
87 let runestone = Runestone::decipher(&transaction);
88
89 if self.compact {
90 Ok(Some(Box::new(CompactOutput {
91 inscriptions: inscriptions
92 .clone()
93 .into_iter()
94 .map(|inscription| inscription.payload.try_into())
95 .collect::<Result<Vec<CompactInscription>>>()?,
96 runestone,
97 })))
98 } else {
99 Ok(Some(Box::new(RawOutput {
100 inscriptions,
101 runestone,
102 })))
103 }
104 }
105}