use alloc::string::ToString;
use alloc::vec::Vec;
use miden_protocol::account::{Account, AccountId};
use miden_protocol::note::{NoteId, NoteTag};
use miden_tx::utils::serde::{
ByteReader,
ByteWriter,
Deserializable,
DeserializationError,
Serializable,
};
use tracing::warn;
use crate::Client;
use crate::errors::ClientError;
use crate::store::{InputNoteRecord, NoteRecordError};
impl<AUTH> Client<AUTH> {
pub async fn get_note_tags(&self) -> Result<Vec<NoteTagRecord>, ClientError> {
self.store.get_note_tags().await.map_err(Into::into)
}
pub async fn add_note_tag(&mut self, tag: NoteTag) -> Result<(), ClientError> {
match self.insert_note_tag(NoteTagRecord { tag, source: NoteTagSource::User }).await {
Ok(true) => Ok(()),
Ok(false) => {
warn!("Tag {} is already being tracked", tag);
Ok(())
},
Err(err) => Err(err),
}
}
pub async fn insert_note_tag(&self, tag_record: NoteTagRecord) -> Result<bool, ClientError> {
self.check_note_tag_limit().await?;
self.store.add_note_tag(tag_record).await.map_err(Into::into)
}
pub async fn remove_note_tag(&mut self, tag: NoteTag) -> Result<(), ClientError> {
if self
.store
.remove_note_tag(NoteTagRecord { tag, source: NoteTagSource::User })
.await?
== 0
{
warn!("Tag {} wasn't being tracked", tag);
}
Ok(())
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct NoteTagRecord {
pub tag: NoteTag,
pub source: NoteTagSource,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum NoteTagSource {
Account(AccountId),
Note(NoteId),
User,
}
impl NoteTagRecord {
pub fn with_note_source(tag: NoteTag, note_id: NoteId) -> Self {
Self {
tag,
source: NoteTagSource::Note(note_id),
}
}
pub fn with_account_source(tag: NoteTag, account_id: AccountId) -> Self {
Self {
tag,
source: NoteTagSource::Account(account_id),
}
}
}
impl Serializable for NoteTagSource {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
match self {
NoteTagSource::Account(account_id) => {
target.write_u8(0);
account_id.write_into(target);
},
NoteTagSource::Note(note_id) => {
target.write_u8(1);
note_id.write_into(target);
},
NoteTagSource::User => target.write_u8(2),
}
}
}
impl Deserializable for NoteTagSource {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
match source.read_u8()? {
0 => Ok(NoteTagSource::Account(AccountId::read_from(source)?)),
1 => Ok(NoteTagSource::Note(NoteId::read_from(source)?)),
2 => Ok(NoteTagSource::User),
val => Err(DeserializationError::InvalidValue(format!("Invalid tag source: {val}"))),
}
}
}
impl PartialEq<NoteTag> for NoteTagRecord {
fn eq(&self, other: &NoteTag) -> bool {
self.tag == *other
}
}
impl From<&Account> for NoteTagRecord {
fn from(account: &Account) -> Self {
NoteTagRecord::with_account_source(NoteTag::with_account_target(account.id()), account.id())
}
}
impl TryInto<NoteTagRecord> for &InputNoteRecord {
type Error = NoteRecordError;
fn try_into(self) -> Result<NoteTagRecord, Self::Error> {
match self.metadata() {
Some(metadata) => Ok(NoteTagRecord::with_note_source(metadata.tag(), self.id())),
None => Err(NoteRecordError::ConversionError(
"Input Note Record does not contain tag".to_string(),
)),
}
}
}