use alloc::boxed::Box;
use alloc::string::String;
use core::future::Future;
use core::pin::Pin;
use crate::error::Error;
use crate::prelude::*;
pub trait IntoEventBuilder: Sized {
fn into_event_builder(self) -> EventBuilder;
}
impl<B> FinalizeUnsignedEvent for B
where
B: IntoEventBuilder,
{
#[inline]
fn finalize_unsigned(self, public_key: PublicKey) -> UnsignedEvent {
let builder: EventBuilder = self.into_event_builder();
builder.finalize_unsigned(public_key)
}
}
impl<B, S> FinalizeEvent<S> for B
where
B: IntoEventBuilder,
S: GetPublicKey + SignEvent + ?Sized,
{
type Error = Error;
fn finalize(self, signer: &S) -> Result<Event, Self::Error> {
let builder: EventBuilder = self.into_event_builder();
builder.finalize(signer)
}
}
impl<B, S> FinalizeEventAsync<S> for B
where
B: IntoEventBuilder + Send,
S: AsyncGetPublicKey + AsyncSignEvent + ?Sized,
{
type Error = Error;
fn finalize_async<'a>(
self,
signer: &'a S,
) -> Pin<Box<dyn Future<Output = Result<Event, Error>> + Send + 'a>>
where
Self: 'a,
S: 'a,
{
Box::pin(async move {
let builder: EventBuilder = self.into_event_builder();
builder.finalize_async(signer).await
})
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct EventBuilder {
pub kind: Kind,
pub tags: Tags,
pub content: String,
pub created_at: Option<Timestamp>,
}
impl EventBuilder {
#[inline]
pub fn new<S>(kind: Kind, content: S) -> Self
where
S: Into<String>,
{
Self {
kind,
tags: Tags::new(),
content: content.into(),
created_at: None,
}
}
#[inline]
pub fn tag(mut self, tag: Tag) -> Self {
self.tags.push(tag);
self
}
pub fn tag_maybe(mut self, tag: Option<Tag>) -> Self {
if let Some(tag) = tag {
self.tags.push(tag);
}
self
}
#[inline]
pub fn tags<I>(mut self, tags: I) -> Self
where
I: IntoIterator<Item = Tag>,
{
self.tags.extend(tags);
self
}
#[inline]
pub fn custom_created_at(mut self, created_at: Timestamp) -> Self {
self.created_at = Some(created_at);
self
}
}
impl FinalizeUnsignedEvent for EventBuilder {
#[inline]
fn finalize_unsigned(self, public_key: PublicKey) -> UnsignedEvent {
UnsignedEvent {
id: None,
pubkey: public_key,
created_at: self.created_at.unwrap_or_else(Timestamp::now),
kind: self.kind,
tags: self.tags,
content: self.content,
}
}
}
impl<S> FinalizeEvent<S> for EventBuilder
where
S: GetPublicKey + SignEvent + ?Sized,
{
type Error = Error;
fn finalize(self, signer: &S) -> Result<Event, Self::Error> {
let public_key: PublicKey = signer.get_public_key().map_err(Error::other)?;
let unsigned: UnsignedEvent = self.finalize_unsigned(public_key);
signer.sign_event(unsigned).map_err(Error::other)
}
}
impl<S> FinalizeEventAsync<S> for EventBuilder
where
S: AsyncGetPublicKey + AsyncSignEvent + ?Sized,
{
type Error = Error;
fn finalize_async<'a>(
self,
signer: &'a S,
) -> Pin<Box<dyn Future<Output = Result<Event, Self::Error>> + Send + 'a>>
where
Self: 'a,
S: 'a,
{
Box::pin(async move {
let public_key: PublicKey =
signer.get_public_key_async().await.map_err(Error::other)?;
let unsigned: UnsignedEvent = self.finalize_unsigned(public_key);
signer
.sign_event_async(unsigned)
.await
.map_err(Error::other)
})
}
}
#[cfg(all(test, feature = "std", feature = "os-rng"))]
mod tests {
use core::str::FromStr;
use super::*;
use crate::SecretKey;
#[test]
fn round_trip() {
let keys = Keys::new(
SecretKey::from_str("6b911fd37cdf5c81d4c0adb1ab7fa822ed253ab0ad9aa18d77257c88b29b718e")
.unwrap(),
);
let event = EventBuilder::new(Kind::TextNote, "hello")
.finalize(&keys)
.unwrap();
let serialized = event.as_json();
let deserialized = Event::from_json(serialized).unwrap();
assert_eq!(event, deserialized);
}
}
#[cfg(bench)]
#[cfg(all(feature = "std", feature = "os-rng"))]
mod benches {
use test::{Bencher, black_box};
use super::*;
#[bench]
pub fn builder_to_event(bh: &mut Bencher) {
let keys = Keys::generate();
bh.iter(|| {
black_box(EventBuilder::new(Kind::TextNote, "hello").finalize(&keys)).unwrap();
});
}
}