1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
//! # notepack
//!
//! A Rust library for packing and parsing [nostr](https://github.com/nostr-protocol/nostr) notes
//! into a compact binary format called **notepack**.
//!
//! This crate provides two core capabilities:
//!
//! - **Encoding**: Turn a [`Note`] (a structured Nostr event) into a notepack binary, or a Base64
//! string prefixed with `notepack_`.
//! - **Decoding / Streaming Parsing**: Efficiently stream through a binary notepack payload using
//! [`NoteParser`], yielding fields as they are parsed (without needing to fully deserialize).
//!
//! ## Features
//!
//! - **Compact binary format** using varint encoding for integers.
//! - **Streaming parser**: no allocation-heavy parsing; fields are yielded one by one as they’re read.
//!
//! ## Example: Encoding a Note
//!
//! ```rust
//! use notepack::{NoteBuf, pack_note_to_string};
//!
//! let note = NoteBuf {
//! id: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".into(),
//! pubkey: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb".into(),
//! created_at: 1753898766,
//! kind: 1,
//! tags: vec![vec!["tag".into(), "value".into()]],
//! content: "Hello, world!".into(),
//! sig: "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc".into(),
//! };
//!
//! let packed = pack_note_to_string(¬e).unwrap();
//! println!("{packed}");
//! // prints something like `notepack_AAECA...`
//! ```
//!
//! ## Example: Streaming Parse
//!
//! ```rust
//! use notepack::{NoteParser, ParsedField};
//!
//! let b64 = "notepack_737yskaxtaKQSL3IPPhOOR8T1R4G/f4ARPHGeNPfOpF4417q9YtU+4JZGOD3+Y0S3uVU6/edo64oTqJQ0pOF29Ms7GmX6fzM4Wjc6sohGPlbdRGLjhuqIRccETX5DliwUFy9qGg2lDD9oMl8ijoNFq4wwJ5Ikmr4Vh7NYWBwOkuo/anEBgECaGkA";
//! let bytes = NoteParser::decode(b64).unwrap();
//! let parser = NoteParser::new(&bytes);
//!
//! for field in parser {
//! match field.unwrap() {
//! ParsedField::Id(id) => println!("id: {}", hex::encode(id)),
//! ParsedField::Content(c) => println!("content: {}", c),
//! _ => {}
//! }
//! }
//! ```
//!
//! ## Binary Tool
//!
//! This crate also ships with a small CLI called `notepack` (see `main.rs`):
//!
//! - **Pipe in a JSON Nostr event** → outputs a `notepack_...` string.
//! - **Pipe in a `notepack_...` string** → outputs the JSON representation.
//!
//! ```bash
//! echo '{"id":"...","pubkey":"...","created_at":123,"kind":1,"tags":[],"content":"Hi","sig":"..."}' \
//! | notepack
//! ```
//!
//! ## Modules
//!
//! - [`Note`] — main event struct used for encoding.
//! - [`NoteParser`] — streaming parser for notepack binaries.
//! - [`ParsedField`] — enum of parsed fields yielded by the parser.
//! - [`Error`] — unified error type.
//! - [`StringType`] — distinguishes between raw byte tags and UTF-8 tags.
//!
//! ## Spec
//!
//! The notepack format is loosely inspired by [MessagePack](https://msgpack.org/) but optimized for
//! Nostr notes. Strings that look like 32-byte hex are stored more compactly; integers are encoded
//! as LEB128-style varints; and the format starts with a `version` field for forward compatibility.
pub use Error;
pub use ;
pub use ;
pub use StringType;
use ;
/// Packs a [`Note`] into its compact binary notepack representation.
///
/// This function serializes a [`Note`] into the raw notepack binary format:
/// - Adds version (currently `1`) as a varint.
/// - Encodes fixed-size fields (`id`, `pubkey`, `sig`) as raw bytes.
/// - Writes variable-length fields (`content`, `tags`) with varint length prefixes.
/// - Optimizes strings that look like 32-byte hex by storing them in a compressed form.
///
/// Returns a `Vec<u8>` containing the binary payload, or an [`Error`] if hex decoding fails.
///
/// This is the low-level encoding API—most callers will want [`pack_note_to_string`] instead.
///
/// # Errors
///
/// Returns [`Error::Hex`] if any hex string field (like `id`, `pubkey`, or `sig`) fails to decode.
///
/// # Example
///
/// ```rust
/// use notepack::{NoteBuf, pack_note};
///
/// let note = NoteBuf::default();
/// let binary = pack_note(¬e).unwrap();
/// assert!(binary.len() > 0);
/// ```
/// Encodes a [`Note`] directly to a `notepack_...` Base64 string.
///
/// This is a convenience wrapper around [`pack_note`], taking the binary payload and
/// Base64-encoding it (without padding) and prefixing with `notepack_`.
///
/// This is the primary API for exporting notes for storage, transmission, or embedding in JSON.
///
/// # Errors
///
/// Returns the same [`Error`]s as [`pack_note`], e.g. hex decoding issues.
///
/// # Example
///
/// ```rust
/// use notepack::{NoteBuf, pack_note_to_string};
///
/// let note = NoteBuf::default();
/// let s = pack_note_to_string(¬e).unwrap();
/// assert!(s.starts_with("notepack_"));
/// ```
/// Only lower cased hex are allowed, otherwise encoding
/// wouldn't round-trip