ordinals_parser/lib.rs
1//! A lightweight parser for Bitcoin Ordinals inscriptions.
2//!
3//! This library allows you to parse and extract Ordinals inscriptions from Bitcoin
4//! transactions without requiring the full Ord codebase.
5//!
6//! # Features
7//!
8//! - Parse Ordinals inscriptions from Bitcoin transactions
9//! - Support for both classic and modern inscription formats
10//! - Extract content type, body, and other metadata
11//! - Detect JSON in text/plain content
12//! - Low dependency footprint
13//!
14//! # Basic Example
15//!
16//! ```rust
17//! use ordinals_parser::{parse_inscriptions_from_transaction, Inscription};
18//! use bitcoin::Transaction;
19//!
20//! # fn example() {
21//! // Load a Bitcoin transaction
22//! let transaction: Transaction = /* ... */;
23//!
24//! // Parse inscriptions
25//! let inscriptions = parse_inscriptions_from_transaction(&transaction);
26//!
27//! for inscription in inscriptions {
28//! println!("Content type: {:?}", inscription.content_type());
29//! println!("Content length: {:?}", inscription.content_length());
30//!
31//! if let Some(body) = inscription.body() {
32//! // Work with inscription content
33//! }
34//! }
35//! # }
36//! ```
37//!
38//! # Working with JSON Inscriptions
39//!
40//! The library can detect and parse JSON content in both `application/json` and `text/plain` inscriptions:
41//!
42//! ```rust
43//! use ordinals_parser::{parse_inscriptions_from_transaction};
44//! use bitcoin::Transaction;
45//! use serde_json::Value;
46//!
47//! # fn example() {
48//! // Load a Bitcoin transaction
49//! let transaction: Transaction = /* ... */;
50//!
51//! // Parse inscriptions
52//! let inscriptions = parse_inscriptions_from_transaction(&transaction);
53//!
54//! for inscription in inscriptions {
55//! if let (Some(content_type), Some(body)) = (inscription.content_type(), inscription.body()) {
56//! // Check if it's JSON content
57//! let is_json = content_type == "application/json" ||
58//! (content_type.starts_with("text/plain") &&
59//! if let Ok(text) = std::str::from_utf8(body) {
60//! let trimmed = text.trim();
61//! (trimmed.starts_with('{') && trimmed.ends_with('}')) ||
62//! (trimmed.starts_with('[') && trimmed.ends_with(']'))
63//! } else {
64//! false
65//! });
66//!
67//! if is_json {
68//! if let Ok(text) = std::str::from_utf8(body) {
69//! if let Ok(json) = serde_json::from_str::<Value>(text) {
70//! // Process BRC-20, Ordinals Collections, etc.
71//! println!("Found JSON content: {:?}", json);
72//! }
73//! }
74//! }
75//! }
76//! }
77//! # }
78//! ```
79//!
80//! # Creating an Inscription
81//!
82//! ```rust
83//! use ordinals_parser::InscriptionBuilder;
84//!
85//! # fn example() {
86//! let inscription = InscriptionBuilder::new()
87//! .content_type("text/plain;charset=utf-8")
88//! .body("Hello, Ordinals!".as_bytes())
89//! .build();
90//!
91//! // Convert to a script (for inclusion in a Bitcoin transaction)
92//! let script = inscription.to_script();
93//! # }
94//! ```
95
96/// Error handling module
97pub mod error;
98
99/// Inscription envelope parsing
100pub mod envelope;
101
102/// Inscription data structure
103pub mod inscription;
104
105/// Inscription ID
106pub mod inscription_id;
107
108/// Tags used in inscriptions
109pub mod tag;
110
111// Re-export important types
112pub use crate::error::{Error, Result};
113pub use crate::inscription::{Inscription, InscriptionBuilder};
114pub use crate::inscription_id::InscriptionId;
115pub use crate::envelope::{
116 parse_transaction_inscriptions,
117 parse_input_inscriptions,
118 is_inscription_input,
119 parse_envelope,
120};
121
122/// Parse all inscriptions from a Bitcoin transaction
123///
124/// This function examines all inputs in the transaction and extracts any
125/// inscriptions found in their witness data.
126///
127/// # Parameters
128///
129/// * `tx` - A reference to a Bitcoin transaction
130///
131/// # Returns
132///
133/// A vector of `Inscription` objects found in the transaction
134///
135/// # Example
136///
137/// ```rust
138/// use ordinals_parser::parse_inscriptions_from_transaction;
139/// use bitcoin::Transaction;
140///
141/// // Load a Bitcoin transaction
142/// let transaction: Transaction = /* ... */;
143///
144/// // Parse all inscriptions
145/// let inscriptions = parse_inscriptions_from_transaction(&transaction);
146/// ```
147pub fn parse_inscriptions_from_transaction(tx: &bitcoin::Transaction) -> Vec<Inscription> {
148 envelope::parse_transaction_inscriptions(tx)
149}
150
151#[cfg(test)]
152mod tests {
153 use super::*;
154 use bitcoin::consensus::deserialize;
155 use hex::FromHex;
156
157 #[test]
158 fn test_parse_basic_inscription() {
159 // Simplified test transaction with an inscription
160 let tx_hex = "0200000000010137c361dc8bdf2c902a73c76f3059db944fc62e635ae7381aa06c79e9a1d1640b0100000000fdffffff020000000000000000296a0e6f7264010100000000000000110011018fe3bfeff84353cd5863c7c9ba48575a0cb2fb606d075f336701000000000017a914121f75e89e9b240a83b303b6e22e074d1c65dfa9870247304402205d793a28c26e568cf0601aae58a976a880f98fae56d905ab9e9e7f7342d4b3d402201b53b8f5507ac93eb7afad7e34af11c78c9b325947ad8c3f25be67700c0c0a90012103af0b2c5c872b732ac9474c576f84950e0a89ec57500507944c87f1fa4af76b9d00000000";
161 let tx_bytes = Vec::from_hex(tx_hex).unwrap();
162 let tx: bitcoin::Transaction = deserialize(&tx_bytes).unwrap();
163
164 let inscriptions = parse_inscriptions_from_transaction(&tx);
165
166 assert_eq!(inscriptions.len(), 1);
167 assert_eq!(inscriptions[0].content_type(), Some("text/plain"));
168 assert_eq!(inscriptions[0].body(), Some(b"Hello, world!".as_ref()));
169 }
170}