use crate::Error;
use crate::parser::read_string;
use crate::stringtype::StringType;
use crate::varint::{read_tagged_varint, read_varint};
use serde::ser::SerializeStruct;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct NoteBuf {
pub id: String,
pub pubkey: String,
pub created_at: u64,
pub kind: u64,
pub tags: Vec<Vec<String>>,
pub content: String,
pub sig: String,
}
#[derive(Debug, Clone)]
pub struct Note<'a> {
pub id: &'a [u8; 32],
pub pubkey: &'a [u8; 32],
pub sig: &'a [u8; 64],
pub content: &'a str,
pub created_at: u64,
pub kind: u64,
pub tags: Tags<'a>,
}
impl<'a> Serialize for Note<'a> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut st = serializer.serialize_struct("Note", 7)?;
st.serialize_field("id", &hex::encode(self.id))?;
st.serialize_field("pubkey", &hex::encode(self.pubkey))?;
st.serialize_field("created_at", &self.created_at)?;
st.serialize_field("kind", &self.kind)?;
let mut tags_json: Vec<Vec<String>> = Vec::with_capacity(self.tags.len() as usize);
let mut tags = self.tags.clone();
while let Some(mut elems) = tags
.next_tag()
.map_err(|e| <S::Error as serde::ser::Error>::custom(e.to_string()))?
{
let mut tag_vec: Vec<String> = Vec::with_capacity(elems.remaining() as usize);
while let Some(elem) = elems
.next()
.transpose()
.map_err(|e| <S::Error as serde::ser::Error>::custom(e.to_string()))?
{
match elem {
crate::stringtype::StringType::Str(s) => tag_vec.push(s.to_string()),
crate::stringtype::StringType::Bytes(bs) => tag_vec.push(hex::encode(bs)),
}
}
tags_json.push(tag_vec);
}
st.serialize_field("tags", &tags_json)?;
st.serialize_field("content", &self.content)?;
st.serialize_field("sig", &hex::encode(self.sig))?;
st.end()
}
}
#[derive(Debug, Clone)]
pub struct Tags<'a> {
data: &'a [u8], remaining: u64, }
#[derive(Debug)]
pub struct TagElems<'a, 'p> {
cursor: &'p mut &'a [u8], remaining: u64, }
impl<'a> Tags<'a> {
pub fn parse(input: &mut &'a [u8]) -> Result<Self, Error> {
let num_tags = read_varint(input)?;
Ok(Self {
data: *input,
remaining: num_tags,
})
}
#[inline]
pub fn len(&self) -> u64 {
self.remaining
}
#[inline]
pub fn is_empty(&self) -> bool {
self.remaining == 0
}
pub fn next_tag<'p>(&'p mut self) -> Result<Option<TagElems<'a, 'p>>, Error> {
if self.remaining == 0 {
return Ok(None);
}
let num_elems = read_varint(&mut self.data)?;
self.remaining -= 1;
Ok(Some(TagElems {
cursor: &mut self.data,
remaining: num_elems,
}))
}
}
impl<'a, 'p> TagElems<'a, 'p> {
#[inline]
pub fn remaining(&self) -> u64 {
self.remaining
}
pub fn finish(mut self) -> Result<(), Error> {
while self.remaining > 0 {
let (len, _is_bytes) = read_tagged_varint(self.cursor)?;
if self.cursor.len() < len as usize {
return Err(Error::Truncated);
}
*self.cursor = &self.cursor[len as usize..];
self.remaining -= 1;
}
Ok(())
}
}
impl<'a, 'p> Iterator for TagElems<'a, 'p> {
type Item = Result<StringType<'a>, Error>;
fn next(&mut self) -> Option<Self::Item> {
if self.remaining == 0 {
return None;
}
let item = read_string(self.cursor);
match item {
Ok(s) => {
self.remaining -= 1;
Some(Ok(s))
}
Err(e) => {
self.remaining = 0;
Some(Err(e))
}
}
}
}
impl<'a, 'p> Drop for TagElems<'a, 'p> {
fn drop(&mut self) {
while self.remaining > 0 {
if let Ok((len, _is_bytes)) = read_tagged_varint(self.cursor) {
if self.cursor.len() < len as usize {
break; }
*self.cursor = &self.cursor[len as usize..];
self.remaining -= 1;
} else {
break; }
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::varint::{write_tagged_varint, write_varint};
fn push_elem_str(buf: &mut Vec<u8>, s: &str) {
write_tagged_varint(buf, s.len() as u64, false);
buf.extend_from_slice(s.as_bytes());
}
fn push_elem_bytes(buf: &mut Vec<u8>, bs: &[u8]) {
write_tagged_varint(buf, bs.len() as u64, true);
buf.extend_from_slice(bs);
}
fn build_tags_block(tags: &[Vec<ElemSpec>]) -> Vec<u8> {
let mut buf = Vec::new();
write_varint(&mut buf, tags.len() as u64);
for tag in tags {
write_varint(&mut buf, tag.len() as u64);
for e in tag {
match e {
ElemSpec::Str(s) => push_elem_str(&mut buf, s),
ElemSpec::Bytes(bs) => push_elem_bytes(&mut buf, bs),
}
}
}
buf
}
#[derive(Debug, Clone)]
enum ElemSpec {
Str(&'static str),
Bytes(&'static [u8]),
}
#[test]
fn tags_iterates_all_elements_correctly() -> Result<(), Error> {
let block = build_tags_block(&[
vec![
ElemSpec::Str("p"),
ElemSpec::Bytes(&[0xaa, 0xbb]),
ElemSpec::Str("hello"),
],
vec![ElemSpec::Str("")],
]);
let mut input = block.as_slice();
let mut tags = Tags::parse(&mut input)?;
assert_eq!(tags.len(), 2);
assert!(!tags.is_empty());
{
let mut t0 = tags.next_tag()?.expect("tag0");
let mut out = Vec::new();
while let Some(x) = t0.next() {
match x? {
StringType::Str(s) => out.push(format!("S:{s}")),
StringType::Bytes(bs) => out.push(format!("B:{}", hex::encode(bs))),
}
}
assert_eq!(out, &["S:p", "B:aabb", "S:hello"]);
}
{
let mut t1 = tags.next_tag()?.expect("tag1");
let got = t1.next().expect("1 elem")?;
match got {
StringType::Str(s) => assert_eq!(s, ""),
_ => panic!("expected empty string"),
}
assert!(t1.next().is_none());
}
assert!(tags.next_tag()?.is_none());
Ok(())
}
#[test]
fn dropping_tag_elems_early_fast_forwards_to_next_tag() -> Result<(), Error> {
let block = build_tags_block(&[
vec![ElemSpec::Str("a"), ElemSpec::Str("b"), ElemSpec::Str("c")],
vec![ElemSpec::Str("z")],
]);
let mut input = block.as_slice();
let mut tags = Tags::parse(&mut input)?;
{
let mut t0 = tags.next_tag()?.expect("tag0");
let first = t0.next().expect("has first")?;
match first {
StringType::Str("a") => {}
_ => panic!("unexpected first element"),
}
}
{
let mut t1 = tags.next_tag()?.expect("tag1");
let first = t1.next().expect("has z")?;
match first {
StringType::Str("z") => {}
_ => panic!("expected 'z'"),
}
assert!(t1.next().is_none());
}
assert!(tags.next_tag()?.is_none());
Ok(())
}
#[test]
fn finish_reports_truncation_error() {
let mut buf = Vec::new();
write_varint(&mut buf, 1); write_varint(&mut buf, 1); write_tagged_varint(&mut buf, 10, false); buf.extend_from_slice(b"abc");
let mut input = buf.as_slice();
let mut tags = Tags::parse(&mut input).expect("parse ok");
let elems = tags.next_tag().expect("ok").expect("tag");
let err = elems.finish().unwrap_err();
match err {
Error::Truncated => {} other => panic!("unexpected error: {other:?}"),
}
}
}