use crate::note_util::NoteUtil; use bech32::{Hrp, NoChecksum}; use egui::TextBuffer; use nostr_sdk::util::hex; use nostrdb::{Filter, Note}; use std::fmt::{Display, Formatter}; #[derive(Clone, Eq, PartialEq)] pub struct NostrLink { pub hrp: NostrLinkType, pub id: IdOrStr, pub kind: Option, pub author: Option<[u8; 32]>, pub relays: Vec, } #[derive(Clone, Eq, PartialEq)] pub enum IdOrStr { Id([u8; 32]), Str(String), } impl Display for IdOrStr { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { IdOrStr::Id(id) => write!(f, "{}", hex::encode(id)), IdOrStr::Str(str) => write!(f, "{}", str), } } } #[derive(Clone, Eq, PartialEq)] pub enum NostrLinkType { Note, PublicKey, PrivateKey, // TLV kinds Event, Profile, Coordinate, } impl NostrLink { pub fn new(hrp: NostrLinkType, id: IdOrStr, kind: Option, author: Option<[u8; 32]>, relays: Vec) -> Self { Self { hrp, id, kind, author, relays, } } pub fn from_note(note: &Note<'_>) -> Self { if note.kind() >= 30_000 && note.kind() < 40_000 { Self { hrp: NostrLinkType::Coordinate, id: IdOrStr::Str(note.get_tag_value("d").unwrap().variant().str().unwrap().to_string()), kind: Some(note.kind()), author: Some(note.pubkey().clone()), relays: vec![], } } else { Self { hrp: NostrLinkType::Event, id: IdOrStr::Id(note.id().clone()), kind: Some(note.kind()), author: Some(note.pubkey().clone()), relays: vec![], } } } pub fn to_tag(&self) -> Vec { if self.hrp == NostrLinkType::Coordinate { vec!["a".to_string(), self.to_tag_value()] } else { vec!["e".to_string(), self.to_tag_value()] } } pub fn to_tag_value(&self) -> String { if self.hrp == NostrLinkType::Coordinate { format!("{}:{}:{}", self.kind.unwrap(), hex::encode(self.author.unwrap()), self.id) } else { self.id.to_string() } } } impl TryInto for &NostrLink { type Error = (); fn try_into(self) -> Result { match self.hrp { NostrLinkType::Coordinate => { Ok(Filter::new() .tags([match self.id { IdOrStr::Str(ref s) => s.to_owned(), IdOrStr::Id(ref i) => hex::encode(i) }], 'd') .build()) } NostrLinkType::Event | NostrLinkType::Note => { Ok(Filter::new().build()) } _ => Err(()) } } } impl Display for NostrLinkType { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { Self::Note => write!(f, "note"), Self::PublicKey => write!(f, "npub"), Self::PrivateKey => write!(f, "nsec"), Self::Event => write!(f, "nevent"), Self::Profile => write!(f, "nprofile"), Self::Coordinate => write!(f, "naddr"), } } } impl NostrLinkType { pub fn to_hrp(&self) -> Hrp { let str = self.to_string(); Hrp::parse(str.as_str()).unwrap() } } impl Display for NostrLink { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self.hrp { NostrLinkType::Note | NostrLinkType::PrivateKey | NostrLinkType::PublicKey => { Ok(bech32::encode_to_fmt::(f, self.hrp.to_hrp(), match &self.id { IdOrStr::Str(s) => s.as_bytes(), IdOrStr::Id(i) => i }).map_err(|e| std::fmt::Error)?) } NostrLinkType::Event | NostrLinkType::Profile | NostrLinkType::Coordinate => todo!() } } }